Current File : /var/www/vinorea/modules/ps_checkout/src/Order/CommandHandler/CreateOrderCommandHandler.php
<?php
/**
 * Copyright since 2007 PrestaShop SA and Contributors
 * PrestaShop is an International Registered Trademark & Property of PrestaShop SA
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License version 3.0
 * that is bundled with this package in the file LICENSE.md.
 * It is also available through the world-wide-web at this URL:
 * https://opensource.org/licenses/AFL-3.0
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@prestashop.com so we can send you a copy immediately.
 *
 * @author    PrestaShop SA and Contributors <contact@prestashop.com>
 * @copyright Since 2007 PrestaShop SA and Contributors
 * @license   https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
 */

namespace PrestaShop\Module\PrestashopCheckout\Order\CommandHandler;

use Cart;
use Currency;
use Exception;
use Order;
use PrestaShop\Module\PrestashopCheckout\Cart\Exception\CartException;
use PrestaShop\Module\PrestashopCheckout\Context\ContextStateManager;
use PrestaShop\Module\PrestashopCheckout\Event\EventDispatcherInterface;
use PrestaShop\Module\PrestashopCheckout\Exception\PsCheckoutException;
use PrestaShop\Module\PrestashopCheckout\FundingSource\FundingSourceTranslationProvider;
use PrestaShop\Module\PrestashopCheckout\Order\Command\CreateOrderCommand;
use PrestaShop\Module\PrestashopCheckout\Order\Event\OrderCreatedEvent;
use PrestaShop\Module\PrestashopCheckout\Order\Exception\OrderException;
use PrestaShop\Module\PrestashopCheckout\Order\Exception\OrderNotFoundException;
use PrestaShop\Module\PrestashopCheckout\Order\Service\CheckOrderAmount;
use PrestaShop\Module\PrestashopCheckout\Order\State\Exception\OrderStateException;
use PrestaShop\Module\PrestashopCheckout\Order\State\OrderStateConfigurationKeys;
use PrestaShop\Module\PrestashopCheckout\Order\State\OrderStateInstaller;
use PrestaShop\Module\PrestashopCheckout\Order\State\Service\OrderStateMapper;
use PrestaShop\Module\PrestashopCheckout\Repository\PsCheckoutCartRepository;
use PrestaShopCollection;
use PrestaShopDatabaseException;
use PrestaShopException;
use Ps_checkout;
use PsCheckoutCart;
use Validate;

class CreateOrderCommandHandler extends AbstractOrderCommandHandler
{
    /**
     * @var EventDispatcherInterface
     */
    private $eventDispatcher;

    /**
     * @var ContextStateManager
     */
    private $contextStateManager;

    /**
     * @var PsCheckoutCartRepository
     */
    private $psCheckoutCartRepository;

    /**
     * @var OrderStateMapper
     */
    private $psOrderStateMapper;

    /**
     * @var Ps_checkout
     */
    private $module;

    /**
     * @var CheckOrderAmount
     */
    private $checkOrderAmount;
    /**
     * @var FundingSourceTranslationProvider
     */
    private $fundingSourceTranslationProvider;

    public function __construct(
        ContextStateManager $contextStateManager,
        EventDispatcherInterface $eventDispatcher,
        PsCheckoutCartRepository $psCheckoutCartRepository,
        OrderStateMapper $psOrderStateMapper,
        Ps_checkout $module,
        CheckOrderAmount $checkOrderAmount,
        FundingSourceTranslationProvider $fundingSourceTranslationProvider
    ) {
        $this->contextStateManager = $contextStateManager;
        $this->eventDispatcher = $eventDispatcher;
        $this->psCheckoutCartRepository = $psCheckoutCartRepository;
        $this->psOrderStateMapper = $psOrderStateMapper;
        $this->module = $module;
        $this->checkOrderAmount = $checkOrderAmount;
        $this->fundingSourceTranslationProvider = $fundingSourceTranslationProvider;
    }

    /**
     * @param CreateOrderCommand $command
     *
     * @return void
     *
     * @throws CartException
     * @throws OrderException
     * @throws OrderNotFoundException
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     * @throws PsCheckoutException
     */
    public function handle(CreateOrderCommand $command)
    {
        /** @var PsCheckoutCart $psCheckoutCart */
        $psCheckoutCart = $this->psCheckoutCartRepository->findOneByPayPalOrderId($command->getOrderPayPalId()->getValue());

        if (Validate::isLoadedObject($this->contextStateManager->getContext()->cart) && (int) $this->contextStateManager->getContext()->cart->id === $psCheckoutCart->getIdCart()) {
            $cart = $this->contextStateManager->getContext()->cart;
        } else {
            $cart = new Cart($psCheckoutCart->getIdCart());
        }

        if (!Validate::isLoadedObject($cart)) {
            throw new PsCheckoutException('Cart not found', PsCheckoutException::PRESTASHOP_CART_NOT_FOUND);
        }

        $orders = new PrestaShopCollection(Order::class);
        $orders->where('id_cart', '=', (int) $cart->id);

        if ($orders->count()) {
            return;
        }

        $products = $cart->getProducts(true);

        if (empty($products)) {
            throw new PsCheckoutException(sprintf('Cart with id %s has no product. Cannot create the order.', var_export($cart->id, true)), PsCheckoutException::CART_PRODUCT_MISSING);
        }

        $fundingSource = $psCheckoutCart->getPaypalFundingSource();
        $transactionId = $orderStateId = $paidAmount = '';
        $capture = $command->getCapturePayPal();
        $currencyId = (int) $cart->id_currency;

        if ($capture) {
            $transactionId = $capture['id'];
            $paidAmount = $capture['status'] === 'COMPLETED' ? $capture['amount']['value'] : '';
            $currencyId = Currency::getIdByIsoCode($capture['amount']['currency_code'], (int) $cart->id_shop);
        }

        try {
            if ($paidAmount) {
                switch ($this->checkOrderAmount->checkAmount((string) $paidAmount, (string) $cart->getOrderTotal(true, \Cart::BOTH))) {
                    case CheckOrderAmount::ORDER_NOT_FULL_PAID:
                        $orderStateId = $this->psOrderStateMapper->getIdByKey(OrderStateConfigurationKeys::PS_CHECKOUT_STATE_PARTIALLY_PAID);
                        break;
                    case CheckOrderAmount::ORDER_FULL_PAID:
                        $orderStateId = $this->psOrderStateMapper->getIdByKey(OrderStateConfigurationKeys::PS_CHECKOUT_STATE_COMPLETED);
                        break;
                    case CheckOrderAmount::ORDER_TO_MUCH_PAID:
                        $orderStateId = $this->psOrderStateMapper->getIdByKey(OrderStateConfigurationKeys::PS_CHECKOUT_STATE_COMPLETED);
                }
            } else {
                $orderStateId = $this->psOrderStateMapper->getIdByKey(OrderStateConfigurationKeys::PS_CHECKOUT_STATE_PENDING);
            }
        } catch (OrderStateException $exception) {
            if ($exception->getCode() === OrderStateException::INVALID_MAPPING) {
                (new OrderStateInstaller())->install();
            }
            $orderStateId = $this->psOrderStateMapper->getIdByKey(OrderStateConfigurationKeys::PS_CHECKOUT_STATE_PENDING);
        }

        if ($this->shouldSetCartContext($this->contextStateManager->getContext(), $cart)) {
            $this->setCartContext($this->contextStateManager, $cart);
        }

        $extraVars = [];

        // Transaction identifier is needed only when an OrderPayment will be created
        // It requires a positive paid amount and an OrderState that's consider the associated order as validated.
        if ($paidAmount && $transactionId) {
            $extraVars['transaction_id'] = $transactionId;
        }

        try {
            $this->module->validateOrder(
                (int) $cart->id,
                $orderStateId,
                $paidAmount,
                $this->fundingSourceTranslationProvider->getPaymentMethodName($fundingSource),
                null,
                $extraVars,
                $currencyId,
                false,
                $cart->secure_key
            );
        } catch (Exception $exception) {
            throw new OrderException(sprintf('Failed to create order from Cart #%s.', var_export($cart->id, true)), OrderException::FAILED_ADD_ORDER, $exception);
        }

        $orders = new PrestaShopCollection(Order::class);
        $orders->where('id_cart', '=', (int) $cart->id);

        if (!$orders->count()) {
            throw new OrderNotFoundException(sprintf('Unable to retrieve order identifier from Cart #%s.', var_export($cart->id, true)), OrderNotFoundException::NOT_FOUND);
        }

        foreach ($orders as $order) {
            $this->eventDispatcher->dispatch(new OrderCreatedEvent((int) $order->id, (int) $cart->id));
        }
    }
}