Aerendir/bundle-features

View on GitHub
src/Model/Feature/Property/HasUnatantumPricesProperty.php

Summary

Maintainability
A
25 mins
Test Coverage
<?php

declare(strict_types=1);

/*
 * This file is part of the Serendipity HQ Features Bundle.
 *
 * Copyright (c) Adamo Aerendir Crespi <aerendir@serendipityhq.com>.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SerendipityHQ\Bundle\FeaturesBundle\Model\Feature\Property;

use Money\Currency;
use SerendipityHQ\Bundle\FeaturesBundle\Model\Feature\Configured\ConfiguredRechargeableFeature;
use SerendipityHQ\Bundle\FeaturesBundle\Model\Feature\Configured\ConfiguredRechargeableFeaturePack;
use SerendipityHQ\Bundle\FeaturesBundle\Model\Feature\FeatureInterface;
use SerendipityHQ\Component\ValueObjects\Money\Money;
use SerendipityHQ\Component\ValueObjects\Money\MoneyInterface;

use function Safe\sprintf;

/**
 * Manages simple prices.
 *
 * Simple prices don't have a subscription interval and are a simple pair of currency => money value.
 */
trait HasUnatantumPricesProperty
{
    /** @var array $grossPrices */
    private $grossPrices = [];

    /** @var array $netPrices */
    private $netPrices = [];

    /** @var string $pricesType */
    private $pricesType;

    /** @var string $taxName */
    private $taxName;

    /** @var float $taxRate */
    private $taxRate;

    /**
     * @param Currency|string $currency This is not typecasted so the method can be called from inside Twig templates simply passing a string
     *
     * @return MoneyInterface|null if the price is not set in the required currency
     */
    public function getPrice($currency, string $type = null): ?MoneyInterface
    {
        if ($currency instanceof Currency) {
            $currency = $currency->getCode();
        }

        if (null === $type) {
            $type = $this->pricesType;
        }

        return $this->getPrices($type)[$currency] ?? new Money([MoneyInterface::BASE_AMOUNT => 0, MoneyInterface::CURRENCY => new Currency($currency)]);
    }

    public function getTaxName(): string
    {
        return $this->taxName;
    }

    public function getTaxRate(): float
    {
        return $this->taxRate;
    }

    public function getPrices(string $type = null): array
    {
        if (null === $type) {
            $type = $this->pricesType;
        }

        switch ($type) {
            case FeatureInterface::PRICE_GROSS:
                return $this->grossPrices;
            case FeatureInterface::PRICE_NET:
                return $this->netPrices;
            default:
                throw new \InvalidArgumentException(sprintf('The prices can be only "net" or "gross". You asked for "%s" prices.', $type));
        }
    }

    /**
     * @param Currency|string $currency This is not typecasted so the method can be called from inside Twig templates simply passing a string
     */
    public function hasPrice($currency, string $type = null): bool
    {
        if ($currency instanceof Currency) {
            $currency = $currency->getCode();
        }

        if (null === $type) {
            $type = $this->pricesType;
        }

        return isset($this->getPrices($type)[$currency]);
    }

    public function setTax(float $rate, string $name): HasUnatantumPricesInterface
    {
        $this->taxName = $name;
        $this->taxRate = $rate;

        $pricesProperty = FeatureInterface::PRICE_NET === $this->pricesType ? 'netPrices' : 'grossPrices';
        // ... Then we have to set gross prices
        if (0 < \count($this->$pricesProperty)) {
            /** @var MoneyInterface $price */
            foreach ($this->$pricesProperty as $currency => $price) {
                switch ($this->pricesType) {
                    // If currently is "net"...
                    case FeatureInterface::PRICE_NET:
                        $netPrice                     = (int) \round($price->getBaseAmount() * (1 + $rate));
                        $netPrice                     = new Money([MoneyInterface::BASE_AMOUNT => $netPrice, MoneyInterface::CURRENCY => $currency]);
                        $this->grossPrices[$currency] = $netPrice;

                        break;
                        // If currently is "gross"...
                    case FeatureInterface::PRICE_GROSS:
                        // ... Then we have to set net prices
                        $grossPrice                 = (int) \round($price->getBaseAmount() / (1 + $rate));
                        $grossPrice                 = new Money([MoneyInterface::BASE_AMOUNT => $grossPrice, MoneyInterface::CURRENCY => $currency]);
                        $this->netPrices[$currency] = $grossPrice;

                        break;
                }
            }
        }

        return $this;
    }

    /**
     * @return ConfiguredRechargeableFeature|ConfiguredRechargeableFeaturePack
     */
    private function setPrices(array $prices, string $pricesType)
    {
        $this->pricesType = $pricesType;
        $priceProperty    = FeatureInterface::PRICE_NET === $this->pricesType ? 'netPrices' : 'grossPrices';

        foreach ($prices as $currency => $price) {
            $this->$priceProperty[$currency] = new Money([MoneyInterface::BASE_AMOUNT => $price, MoneyInterface::CURRENCY => new Currency($currency)]);
        }

        /** @var ConfiguredRechargeableFeature|ConfiguredRechargeableFeaturePack $this */
        return $this;
    }
}