<commit_before><?php

namespace Ekyna\Component\Commerce\Pricing\Resolver;

use Ekyna\Component\Commerce\Common\Model\CountryInterface;
use Ekyna\Component\Commerce\Common\Model\SaleInterface;
use Ekyna\Component\Commerce\Common\Repository\CountryRepositoryInterface;
use Ekyna\Component\Commerce\Customer\Model\CustomerInterface;
use Ekyna\Component\Commerce\Exception\InvalidArgumentException;
use Ekyna\Component\Commerce\Pricing\Model\TaxableInterface;
use Ekyna\Component\Commerce\Pricing\Model\VatNumberSubjectInterface;
use Ekyna\Component\Commerce\Pricing\Repository\TaxRuleRepositoryInterface;

/**
 * Class TaxResolver
 * @package Ekyna\Component\Commerce\Pricing\Resolver
 * @author  <NAME> <<EMAIL>>
 */
class TaxResolver implements TaxResolverInterface
{
    /**
     * @var CountryRepositoryInterface
     */
    protected $countryRepository;

    /**
     * @var TaxRuleRepositoryInterface
     */
    protected $taxRuleRepository;

    /**
     * @var array
     */
    protected $cache;


    /**
     * Constructor.
     *
     * @param CountryRepositoryInterface $countryRepository
     * @param TaxRuleRepositoryInterface $taxRuleRepository
     */
    public function __construct(
        CountryRepositoryInterface $countryRepository,
        TaxRuleRepositoryInterface $taxRuleRepository
    ) {
        $this->countryRepository = $countryRepository;
        $this->taxRuleRepository = $taxRuleRepository;

        $this->cache = new ResolvedTaxesCache();
    }

    /**
     * @inheritdoc
     */
    public function resolveTaxes(TaxableInterface $taxable, $target = null)
    {
        // Abort if taxable does not have tax group
        if (null === $taxGroup = $taxable->getTaxGroup()) {
            return [];
        }

        // Abort if tax group does not have taxes
        if (!$taxGroup->hasTaxes()) {
            return [];
        }

        $country = $this->resolveTargetCountry($target);
        $business = $target instanceof VatNumberSubjectInterface
            ? $target->isBusiness()
            : false;

        // Use cached resolved taxes if any
        if (null !== $taxes = $this->cache->get($taxGroup, $country, $business)) {
            return $taxes;
        }

        // Abort if no matching tax rule
        if (null === $taxRule = $this->resolveTaxRule($country, $business)) {
            return [];
        }

        // Abort if tax rule does not have taxes
        if (!$taxRule->hasTaxes()) {
            return [];
        }

        // Resolves the taxes
        $applicableTaxes = $taxRule->getTaxes()->toArray();
        $resolvedTaxes = [];
        foreach ($taxGroup->getTaxes() as $tax) {
            if (in_array($tax, $applicableTaxes, true)) {
                $resolvedTaxes[] = $tax;
            }
        }

        // Caches the resolved taxes
        $this->cache->set($taxGroup, $country, $business, $resolvedTaxes);

        return $resolvedTaxes;
    }

    /**
     * Resolves the sale's tax rule.
     *
     * @param SaleInterface $sale
     *
     * @return \Ekyna\Component\Commerce\Pricing\Model\TaxRuleInterface|null
     */
    public function resolveSaleTaxRule(SaleInterface $sale)
    {
        if (null === $country = $this->resolveSaleTargetCountry($sale)) {
            return null;
        }

        return $this->resolveTaxRule($country, $sale->isBusiness());
    }

    /**
     * Returns the fallback country.
     *
     * @return CountryInterface
     */
    protected function getFallbackCountry()
    {
        return $this->countryRepository->findDefault();
    }

    /**
     * Resolves the target country.
     *
     * @param mixed $target
     *
     * @return CountryInterface
     */
    protected function resolveTargetCountry($target)
    {
        if ($target instanceof CountryInterface) {
            return $target;
        }

        if ($target instanceof SaleInterface) {
            $country = $this->resolveSaleTargetCountry($target);
        } elseif ($target instanceof CustomerInterface) {
            $country = $this->resolveCustomerTargetCountry($target);
        } elseif(is_string($target) && 2 == strlen($target)) {
            $country = $this->getCountryByCode($target);
        } else {
            throw new InvalidArgumentException("Unexpected taxation target.");
        }

        return $country ?: $this->getFallbackCountry();
    }

    /**
     * Resolves the tax rule for the given sale.
     *
     * @param CountryInterface $country
     * @param bool             $business
     *
     * @return \Ekyna\Component\Commerce\Pricing\Model\TaxRuleInterface|null
     */
    protected function resolveTaxRule(CountryInterface $country, $business = false)
    {
        if ($business) {
            return $this->taxRuleRepository->findOneByCountryForBusiness($country);
        }

        return $this->taxRuleRepository->findOneByCountryForCustomer($country);
    }

    /**
     * Resolves the sales's taxation target country.
     *
     * @param SaleInterface $sale
     *
     * @return CountryInterface|null
     */
    protected function resolveSaleTargetCountry(SaleInterface $sale)
    {
        // Get the country from the sale's delivery address
        if (null !== $country = $sale->getDeliveryCountry()) {
            return $country;
        }

        // If none, resolves the customer's taxation target address
        if (null !== $customer = $sale->getCustomer()) {
            return $this->resolveCustomerTargetCountry($customer);
        }

        return null;
    }

    /**
     * Resolves the customer's taxation target country.
     *
     * @param CustomerInterface $customer
     *
     * @return CountryInterface|null
     */
    protected function resolveCustomerTargetCountry(CustomerInterface $customer)
    {
        if (null !== $address = $customer->getDefaultDeliveryAddress()) {
            return $address->getCountry();
        }

        return null;
    }

    /**
     * Returns the country by its code.
     *
     * @param string $code
     *
     * @return CountryInterface|null
     */
    protected function getCountryByCode($code)
    {
        return $this->countryRepository->findOneByCode($code);
    }
}
<commit_msg>[Commerce] Fixed checkout controller (sale view message for default shipment).<commit_after><?php

namespace Ekyna\Component\Commerce\Pricing\Resolver;

use Ekyna\Component\Commerce\Common\Model\CountryInterface;
use Ekyna\Component\Commerce\Common\Model\SaleInterface;
use Ekyna\Component\Commerce\Common\Repository\CountryRepositoryInterface;
use Ekyna\Component\Commerce\Customer\Model\CustomerInterface;
use Ekyna\Component\Commerce\Exception\InvalidArgumentException;
use Ekyna\Component\Commerce\Pricing\Model\TaxableInterface;
use Ekyna\Component\Commerce\Pricing\Model\VatNumberSubjectInterface;
use Ekyna\Component\Commerce\Pricing\Repository\TaxRuleRepositoryInterface;

/**
 * Class TaxResolver
 * @package Ekyna\Component\Commerce\Pricing\Resolver
 * @author  <NAME> <<EMAIL>>
 */
class TaxResolver implements TaxResolverInterface
{
    /**
     * @var CountryRepositoryInterface
     */
    protected $countryRepository;

    /**
     * @var TaxRuleRepositoryInterface
     */
    protected $taxRuleRepository;

    /**
     * @var array
     */
    protected $cache;


    /**
     * Constructor.
     *
     * @param CountryRepositoryInterface $countryRepository
     * @param TaxRuleRepositoryInterface $taxRuleRepository
     */
    public function __construct(
        CountryRepositoryInterface $countryRepository,
        TaxRuleRepositoryInterface $taxRuleRepository
    ) {
        $this->countryRepository = $countryRepository;
        $this->taxRuleRepository = $taxRuleRepository;

        $this->cache = new ResolvedTaxesCache();
    }

    /**
     * @inheritdoc
     */
    public function resolveTaxes(TaxableInterface $taxable, $target = null)
    {
        // TODO @param ContextInterface $context (instead of $target)

        // Abort if taxable does not have tax group
        if (null === $taxGroup = $taxable->getTaxGroup()) {
            return [];
        }

        // Abort if tax group does not have taxes
        if (!$taxGroup->hasTaxes()) {
            return [];
        }

        $country = $this->resolveTargetCountry($target);
        $business = $target instanceof VatNumberSubjectInterface
            ? $target->isBusiness()
            : false;

        // Use cached resolved taxes if any
        if (null !== $taxes = $this->cache->get($taxGroup, $country, $business)) {
            return $taxes;
        }

        // Abort if no matching tax rule
        if (null === $taxRule = $this->resolveTaxRule($country, $business)) {
            return [];
        }

        // Abort if tax rule does not have taxes
        if (!$taxRule->hasTaxes()) {
            return [];
        }

        // Resolves the taxes
        $applicableTaxes = $taxRule->getTaxes()->toArray();
        $resolvedTaxes = [];
        foreach ($taxGroup->getTaxes() as $tax) {
            if (in_array($tax, $applicableTaxes, true)) {
                $resolvedTaxes[] = $tax;
            }
        }

        // Caches the resolved taxes
        $this->cache->set($taxGroup, $country, $business, $resolvedTaxes);

        return $resolvedTaxes;
    }

    /**
     * Resolves the sale's tax rule.
     *
     * @param SaleInterface $sale
     *
     * @return \Ekyna\Component\Commerce\Pricing\Model\TaxRuleInterface|null
     */
    public function resolveSaleTaxRule(SaleInterface $sale)
    {
        if (null === $country = $this->resolveSaleTargetCountry($sale)) {
            return null;
        }

        return $this->resolveTaxRule($country, $sale->isBusiness());
    }

    /**
     * Returns the fallback country.
     *
     * @return CountryInterface
     */
    protected function getFallbackCountry()
    {
        return $this->countryRepository->findDefault();
    }

    /**
     * Resolves the target country.
     *
     * @param mixed $target
     *
     * @return CountryInterface
     */
    protected function resolveTargetCountry($target)
    {
        if ($target instanceof CountryInterface) {
            return $target;
        }

        if ($target instanceof SaleInterface) {
            $country = $this->resolveSaleTargetCountry($target);
        } elseif ($target instanceof CustomerInterface) {
            $country = $this->resolveCustomerTargetCountry($target);
        } elseif(is_string($target) && 2 == strlen($target)) {
            $country = $this->getCountryByCode($target);
        } else {
            throw new InvalidArgumentException("Unexpected taxation target.");
        }

        return $country ?: $this->getFallbackCountry();
    }

    /**
     * Resolves the tax rule for the given sale.
     *
     * @param CountryInterface $country
     * @param bool             $business
     *
     * @return \Ekyna\Component\Commerce\Pricing\Model\TaxRuleInterface|null
     */
    protected function resolveTaxRule(CountryInterface $country, $business = false)
    {
        if ($business) {
            return $this->taxRuleRepository->findOneByCountryForBusiness($country);
        }

        return $this->taxRuleRepository->findOneByCountryForCustomer($country);
    }

    /**
     * Resolves the sales's taxation target country.
     *
     * @param SaleInterface $sale
     *
     * @return CountryInterface|null
     */
    protected function resolveSaleTargetCountry(SaleInterface $sale)
    {
        // Get the country from the sale's delivery address
        if (null !== $country = $sale->getDeliveryCountry()) {
            return $country;
        }

        // If none, resolves the customer's taxation target address
        if (null !== $customer = $sale->getCustomer()) {
            return $this->resolveCustomerTargetCountry($customer);
        }

        return null;
    }

    /**
     * Resolves the customer's taxation target country.
     *
     * @param CustomerInterface $customer
     *
     * @return CountryInterface|null
     */
    protected function resolveCustomerTargetCountry(CustomerInterface $customer)
    {
        if (null !== $address = $customer->getDefaultDeliveryAddress()) {
            return $address->getCountry();
        }

        return null;
    }

    /**
     * Returns the country by its code.
     *
     * @param string $code
     *
     * @return CountryInterface|null
     */
    protected function getCountryByCode($code)
    {
        return $this->countryRepository->findOneByCode($code);
    }
}

{'fraction_non_alphanumeric': 0.11328871892925431, 'fraction_numerical': 0.00015933715742511153, 'mean_word_length': 2.3600107066381155, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 81, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5903592', 'n_tokens_mistral': 3397, 'n_tokens_neox': 3287, 'n_words': 726}