Newer
Older
TillQliro / Model / Fee.php
@Jonas Jonsson Jonas Jonsson on 2 Apr 2024 14 KB Initial
<?php
/**
 * Copyright © Qliro AB. All rights reserved.
 * See LICENSE.txt for license details.
 */

namespace Qliro\QliroOne\Model;

use Magento\Checkout\Model\Session;
use Magento\Customer\Api\Data\AddressInterfaceFactory;
use Magento\Customer\Api\Data\RegionInterfaceFactory;
use Magento\Customer\Api\GroupRepositoryInterface;
use Magento\Framework\Pricing\PriceCurrencyInterface;
use Magento\Catalog\Helper\Data as CatalogHelper;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Customer\Model\Session as CustomerSession;
use Magento\Tax\Api\Data\QuoteDetailsInterfaceFactory;
use Magento\Tax\Api\Data\QuoteDetailsItemInterfaceFactory;
use Magento\Tax\Api\Data\TaxClassKeyInterfaceFactory;
use Magento\Tax\Api\TaxCalculationInterface;
use Magento\Tax\Api\Data\TaxClassKeyInterface;

class Fee
{
    /**
     * @var array
     */
    private $methodsWithFee = [];

    /**
     * @var Config
     */
    private $config;

    /**
     * @var Session
     */
    private $checkoutSession;

    /**
     * @var PriceCurrencyInterface
     */
    private $priceCurrency;

    /**
     * @var CatalogHelper
     */
    private $catalogHelper;

    /**
     * @var StoreManagerInterface
     */
    private $storeManager;

    /**
     * @var CustomerSession
     */
    private $customerSession;

    /**
     * @var TaxClassKeyInterfaceFactory
     */
    private $taxClassKeyFactory;

    /**
     * @var QuoteDetailsInterfaceFactory
     */
    private $quoteDetailsFactory;

    /**
     * @var QuoteDetailsItemInterfaceFactory
     */
    private $quoteDetailsItemFactory;

    /**
     * @var TaxCalculationInterface
     */
    private $taxCalculation;

    /**
     * @var AddressInterfaceFactory
     */
    private $addressFactory;

    /**
     * @var RegionInterfaceFactory
     */
    private $regionFactory;

    /**
     * @var GroupRepositoryInterface
     */
    private $customerGroupRepository;

    /**
     * Fee constructor.
     *
     * @param Config $config
     * @param Session $checkoutSession
     * @param PriceCurrencyInterface $priceCurrency
     * @param CatalogHelper $catalogHelper
     * @param StoreManagerInterface $storeManager
     * @param CustomerSession $customerSession
     * @param TaxClassKeyInterfaceFactory $taxClassKeyFactory
     * @param QuoteDetailsInterfaceFactory $quoteDetailsFactory
     * @param QuoteDetailsItemInterfaceFactory $quoteDetailsItemFactory
     * @param TaxCalculationInterface $taxCalculation
     * @param AddressInterfaceFactory $addressFactory
     * @param RegionInterfaceFactory $regionFactory
     * @param GroupRepositoryInterface $customerGroupRepository
     */
    public function __construct(
        Config $config,
        Session $checkoutSession,
        PriceCurrencyInterface $priceCurrency,
        CatalogHelper $catalogHelper,
        StoreManagerInterface $storeManager,
        CustomerSession $customerSession,
        TaxClassKeyInterfaceFactory $taxClassKeyFactory,
        QuoteDetailsInterfaceFactory $quoteDetailsFactory,
        QuoteDetailsItemInterfaceFactory $quoteDetailsItemFactory,
        TaxCalculationInterface $taxCalculation,
        AddressInterfaceFactory $addressFactory,
        RegionInterfaceFactory $regionFactory,
        GroupRepositoryInterface $customerGroupRepository
    ) {
        $this->config = $config;
        $this->checkoutSession = $checkoutSession;
        $this->priceCurrency = $priceCurrency;
        $this->catalogHelper = $catalogHelper;
        $this->storeManager = $storeManager;
        $this->customerSession = $customerSession;
        $this->taxClassKeyFactory = $taxClassKeyFactory;
        $this->quoteDetailsFactory = $quoteDetailsFactory;
        $this->quoteDetailsItemFactory = $quoteDetailsItemFactory;
        $this->taxCalculation = $taxCalculation;
        $this->addressFactory = $addressFactory;
        $this->regionFactory = $regionFactory;
        $this->customerGroupRepository = $customerGroupRepository;
    }

    /**
     * Sets the fee including Tax, quote should be recalculated after this, to update all remaining fields
     *
     * @param \Magento\Quote\Model\Quote $quote
     * @param float $fee
     */
    public function setQlirooneFeeInclTax(\Magento\Quote\Model\Quote $quote, $fee)
    {
        if ($quote->isVirtual()) {
            $quote->getBillingAddress()->setQlirooneFee($fee);
        } else {
            $quote->getShippingAddress()->setQlirooneFee($fee);
        }
    }

    /**
     * Returns the amount of the fee, if defined. It can be fixed or a percent of the order sum
     * This function must not depend on display settings
     *
     * @param \Magento\Quote\Model\Quote $quote
     * @return float|int
     */
    public function getQlirooneFeeInclTax(\Magento\Quote\Model\Quote $quote)
    {
        if ($quote->isVirtual()) {
            $fee = $quote->getBillingAddress()->getQlirooneFee();
        } else {
            $fee = $quote->getShippingAddress()->getQlirooneFee();
        }
        $price = $this->getCalcTaxPrice($quote, $fee, 1);

        return $price;
    }

    /**
     * Return Fee exluding tax
     *
     * @param \Magento\Quote\Model\Quote $quote
     * @return float|int
     */
    public function getQlirooneFeeExclTax(\Magento\Quote\Model\Quote $quote)
    {
        $price = $this->getQlirooneFeeInclTax($quote);
        $price = $this->getCalcTaxPrice($quote, $price, 0);

        return $price;
    }

    /**
     * @todo Improvement. Proper currency conversion to handle display currencies
     *
     * @param \Magento\Quote\Model\Quote $quote
     * @return float|int
     */
    public function getBaseQlirooneFeeInclTax(\Magento\Quote\Model\Quote $quote)
    {
        return $this->getQlirooneFeeInclTax($quote);
    }

    /**
     * @todo Improvement. Proper currency conversion to handle display currencies
     *
     * @param \Magento\Quote\Model\Quote $quote
     * @return float|int
     */
    public function getBaseQlirooneFeeExclTax(\Magento\Quote\Model\Quote $quote)
    {
        return $this->getQlirooneFeeExclTax($quote);
    }

    /**
     * Get the summary for cart and checkout
     *
     * @param \Magento\Quote\Model\Quote $quote
     * @param float $amount
     * @return array|null
     */
    public function getFeeArray($quote, $amount)
    {
        $feeSetup = $this->getFeeSetup($quote->getStoreId());
        if (!$amount) {
// Ideally, returning null should block the fee from appearing, but it doesn't
//            return null;
        }
        $result = [
            'code' => Config::TOTALS_FEE_CODE,
            'title' => __($feeSetup[Config::CONFIG_FEE_TITLE]),
            'value' => $amount,
        ];
        return $result;
    }

    /**
     * Get the object, used for Totals in both FE and BE on orders, creditnotes and invoices
     *
     * @param int $storeId
     * @param float $amount
     * @return \Magento\Framework\DataObject|null
     */
    public function getFeeObject($storeId, $amount)
    {
        $feeSetup = $this->getFeeSetup($storeId);
        if (!$amount) {
            return null;
        }
        if ($feeSetup) {
            $title = __($feeSetup[Config::CONFIG_FEE_TITLE]);
        } else {
            $title = __('Payment fee');
        }
        $result = new \Magento\Framework\DataObject([
                'code' => Config::TOTALS_FEE_CODE,
                'strong' => false,
                'value' => $amount,
                'label' => $title,
        ]);
        return $result;
    }

    /**
     * Will return fee setup, including an amount of zero
     *
     * @param int $storeId
     * @return array
     */
    public function getFeeSetup($storeId)
    {
        if (!$this->config->isActive($storeId)) {
            return null;
        }
        if (!$this->methodsWithFee) {
            $title = $this->config->getFeeMerchantReference();
            $this->methodsWithFee = array(
                Config::CONFIG_FEE_AMOUNT => 0,
                Config::CONFIG_FEE_TITLE => $title,
            );
        }
        return $this->methodsWithFee;
    }

    /**
     * Picks up the amounts from Fees and runs them through the getTaxPrice function,
     * which changes things depending on display settings etc
     *
     * @param \Magento\Quote\Model\Quote $quote
     * @param array $feeCalc
     * @return array
     */
    public function applyDisplayFlagsToFeeArray($quote, $feeCalc)
    {
        if ($feeCalc[Config::CONFIG_FEE_AMOUNT]) {
            $price = $feeCalc[Config::CONFIG_FEE_AMOUNT];
            $feeCalc[Config::CONFIG_FEE_AMOUNT] = $this->getTaxPrice($quote, $price);
        }

        return $feeCalc;
    }

    /**
     * Get current quote from checkout session
     *
     * @return \Magento\Quote\Model\Quote
     */
    public function getQuote()
    {
        return $this->checkoutSession->getQuote();
    }

    /**
     * Returns the price including or excluding tax, depending on flags being sent in and display settings
     *
     * @param \Magento\Quote\Model\Quote $quote
     * @param float $price
     * @param bool|null $includingTax
     * @param bool|null $feeIncludesTax
     * @return float
     */
    private function getTaxPrice($quote, $price, $includingTax = null, $feeIncludesTax = null)
    {
        $pseudoProduct = new \Magento\Framework\DataObject();
        $pseudoProduct->setTaxClassId(
            $this->config->getFeeTaxClass($quote->getStoreId())
        );

        $shippingAddress = null;
        $billingAddress = null;
        $ctc = null;

        if ($feeIncludesTax === null) {
            $feeIncludesTax = $this->config->paymentFeeIncludesTax($quote->getStoreId());
        }

        $price = $this->catalogHelper->getTaxPrice(
            $pseudoProduct,
            $price,
            $includingTax,
            $shippingAddress,
            $billingAddress,
            $ctc,
            $quote->getStoreId(),
            $feeIncludesTax
        );

        return $price;
    }

    /**
     * Returns the price including or excluding tax, NOT depending on display settings
     * Basically a copy of above used function $this->catalogHelper->getTaxPrice
     *
     * @param \Magento\Quote\Model\Quote $quote
     * @param float $price
     * @param bool $includingTax
     * @param bool|null $feeIncludesTax
     * @return float
     */
    private function getCalcTaxPrice($quote, $price, $includingTax, $feeIncludesTax = null)
    {
        if (!$price) {
            return $price;
        }

        $product = new \Magento\Framework\DataObject();
        $product->setTaxClassId(
            $this->config->getFeeTaxClass($quote->getStoreId())
        );

        $shippingAddress = null;
        $billingAddress = null;
        $ctc = null;
        $roundPrice = true;

        $store = $this->storeManager->getStore($quote->getStoreId());
        if ($feeIncludesTax === null) {
            $feeIncludesTax = $this->config->paymentFeeIncludesTax($quote->getStoreId());
        }

        $shippingAddressDataObject = null;
        if ($shippingAddress === null) {
            $shippingAddressDataObject =
                $this->convertDefaultTaxAddress($this->customerSession->getDefaultTaxShippingAddress());
        } elseif ($shippingAddress instanceof \Magento\Customer\Model\Address\AbstractAddress) {
            $shippingAddressDataObject = $shippingAddress->getDataModel();
        }

        $billingAddressDataObject = null;
        if ($billingAddress === null) {
            $billingAddressDataObject =
                $this->convertDefaultTaxAddress($this->customerSession->getDefaultTaxBillingAddress());
        } elseif ($billingAddress instanceof \Magento\Customer\Model\Address\AbstractAddress) {
            $billingAddressDataObject = $billingAddress->getDataModel();
        }

        $taxClassKey = $this->taxClassKeyFactory->create();
        $taxClassKey->setType(TaxClassKeyInterface::TYPE_ID)
            ->setValue($product->getTaxClassId());

        if ($ctc === null && $this->customerSession->getCustomerGroupId() != null) {
            $ctc = $this->customerGroupRepository->getById($this->customerSession->getCustomerGroupId())
                ->getTaxClassId();
        }

        $customerTaxClassKey = $this->taxClassKeyFactory->create();
        $customerTaxClassKey->setType(TaxClassKeyInterface::TYPE_ID)
            ->setValue($ctc);

        $item = $this->quoteDetailsItemFactory->create();
        $item->setQuantity(1)
            ->setCode($product->getSku())
            ->setShortDescription($product->getShortDescription())
            ->setTaxClassKey($taxClassKey)
            ->setIsTaxIncluded($feeIncludesTax)
            ->setType('product')
            ->setUnitPrice($price);

        $quoteDetails = $this->quoteDetailsFactory->create();
        $quoteDetails->setShippingAddress($shippingAddressDataObject)
            ->setBillingAddress($billingAddressDataObject)
            ->setCustomerTaxClassKey($customerTaxClassKey)
            ->setItems([$item])
            ->setCustomerId($this->customerSession->getCustomerId());

        $storeId = null;
        if ($store) {
            $storeId = $store->getId();
        }
        $taxDetails = $this->taxCalculation->calculateTax($quoteDetails, $storeId, $roundPrice);
        $items = $taxDetails->getItems();
        $taxDetailsItem = array_shift($items);

        if ($includingTax) {
            $price = $taxDetailsItem->getPriceInclTax();
        } else {
            $price = $taxDetailsItem->getPrice();
        }

        if ($roundPrice) {
            return $this->priceCurrency->round($price);
        } else {
            return $price;
        }
    }

    /**
     * @param array $taxAddress
     * @return \Magento\Customer\Api\Data\AddressInterface|null
     */
    private function convertDefaultTaxAddress(array $taxAddress = null)
    {
        if (empty($taxAddress)) {
            return null;
        }
        /** @var \Magento\Customer\Api\Data\AddressInterface $addressDataObject */
        $addressDataObject = $this->addressFactory->create()
            ->setCountryId($taxAddress['country_id'])
            ->setPostcode($taxAddress['postcode']);

        if (isset($taxAddress['region_id'])) {
            $addressDataObject->setRegion($this->regionFactory->create()->setRegionId($taxAddress['region_id']));
        }
        return $addressDataObject;
    }

}