diff --git a/src/Calculators/PriceCalc.php b/src/Calculators/PriceCalc.php index dadf839..0ca9f9b 100644 --- a/src/Calculators/PriceCalc.php +++ b/src/Calculators/PriceCalc.php @@ -4,6 +4,10 @@ namespace MiBo\Prices\Calculators; +use BadMethodCallException; +use DomainException; +use MiBo\Prices\Contracts\PriceCalculatorHelper; +use MiBo\Prices\Contracts\PriceComparer; use MiBo\Prices\Contracts\PriceInterface; use MiBo\VAT\Enums\VATRate; use MiBo\VAT\Resolvers\ProxyResolver; @@ -19,9 +23,16 @@ * @since 0.1 * * @no-named-arguments Parameter names are not covered by the backward compatibility promise. + * + * @mixin \MiBo\Prices\Contracts\PriceCalculatorHelper + * @mixin \MiBo\Prices\Contracts\PriceComparer */ class PriceCalc { + private static ?PriceCalculatorHelper $calculatorHelper = null; + + private static ?PriceComparer $comparerHelper = null; + /** * Returns the value of the VAT of the amount. * @@ -35,12 +46,17 @@ public static function getValueOfVAT(PriceInterface $price): int|float return 0; } - return $price->getNumericalValue()->getValue( - $price->getUnit()->getMinorUnitRate() ?? 0 - ) * ProxyResolver::getPercentageOf( + $minorUnitRate = $price->getUnit()->getMinorUnitRate() ?? 0; + $vatValue = round( + $price->getNumericalValue()->getValue($price->getUnit()->getMinorUnitRate() ?? 0) + * ProxyResolver::getPercentageOf( $price->getVAT(), method_exists($price, 'getDateTime') ? $price->getDateTime() : null - ); + ), + $minorUnitRate + ); + + return $minorUnitRate === 0 ? (int) $vatValue : $vatValue; } /** @@ -93,4 +109,89 @@ public static function add(PriceInterface $addend, PriceInterface ...$addends): $vat, ]; } + + /** + * @param PriceCalculatorHelper $calculatorHelper + * + * @return void + */ + public static function setCalculatorHelper(PriceCalculatorHelper $calculatorHelper): void + { + self::$calculatorHelper = $calculatorHelper; + } + + /** + * @param \MiBo\Prices\Contracts\PriceComparer $comparerHelper + * + * @return void + */ + public static function setComparerHelper(PriceComparer $comparerHelper): void + { + self::$comparerHelper = $comparerHelper; + } + + /** + * @param string $name + * @param array $arguments + * + * @return array|bool|object|float|int|null + */ + public static function __callStatic(string $name, array $arguments): mixed + { + $helper = [ + 'round', + 'ceil', + 'floor', + ]; + + if (in_array($name, $helper)) { + if (self::$calculatorHelper === null) { + throw new DomainException('The PriceCalculatorHelper is not set.'); + } + + return self::$calculatorHelper->$name(...$arguments); + } + + $helper = [ + 'checkThat', + 'isLessThan', + 'isNotLessThan', + 'isLessThanOrEqual', + 'isNotLessThanOrEqual', + 'isGreaterThan', + 'isNotGreaterThan', + 'isGreaterThanOrEqual', + 'isNotGreaterThanOrEqual', + 'isEqual', + 'isNotEqual', + 'isBetween', + 'isNotBetween', + 'isBetweenOrEqual', + 'isNotBetweenOrEqual', + 'isInteger', + 'isNotInteger', + 'isFloat', + 'isNotFloat', + 'isEvent', + 'isNotEvent', + 'isOdd', + 'isNotOdd', + 'hasSameValueAs', + 'hasNotSameValueAs', + 'is', + 'isNot', + 'hasSameValueWithVATAs', + 'hasNotSameValueWithVATAs', + ]; + + if (in_array($name, $helper)) { + if (self::$comparerHelper === null) { + throw new DomainException('The PriceComparer is not set.'); + } + + return self::$comparerHelper->$name(...$arguments); + } + + throw new BadMethodCallException('Method ' . $name . ' does not exist.'); + } } diff --git a/src/Contracts/PriceCalculatorHelper.php b/src/Contracts/PriceCalculatorHelper.php new file mode 100644 index 0000000..2d5c1a4 --- /dev/null +++ b/src/Contracts/PriceCalculatorHelper.php @@ -0,0 +1,50 @@ + + * + * @since 1.2 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise. + */ +interface PriceCalculatorHelper +{ + /** + * Rounds the price. + * + * @param \MiBo\Prices\Contracts\PriceInterface $price Price to round. + * @param int $precision Precision of the rounding. + * @param int<1, 4> $mode Rounding mode. + * + * @return array Rounded prices for each VAT category. + */ + public function round(PriceInterface $price, int $precision = 0, int $mode = PHP_ROUND_HALF_UP): array; + + /** + * Rounds the price up. + * + * @param \MiBo\Prices\Contracts\PriceInterface $price Price to round. + * @param int $precision Precision of the rounding. + * + * @return array Rounded prices for each VAT category. + */ + public function ceil(PriceInterface $price, int $precision = 0): array; + + /** + * Rounds the price down. + * + * @param \MiBo\Prices\Contracts\PriceInterface $price Price to round. + * @param int $precision Precision of the rounding. + * + * @return array Rounded prices for each VAT category. + */ + public function floor(PriceInterface $price, int $precision = 0): array; +} diff --git a/src/Contracts/PriceComparer.php b/src/Contracts/PriceComparer.php new file mode 100644 index 0000000..5b56b3f --- /dev/null +++ b/src/Contracts/PriceComparer.php @@ -0,0 +1,84 @@ + + * + * @since 1.2 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise. + */ +interface PriceComparer +{ + public function checkThat(PriceInterface $price): static; + + public function isLessThan(PriceInterface|int|float $price): bool; + + public function isNotLessThan(PriceInterface|int|float $price): bool; + + public function isLessThanOrEqualTo(PriceInterface|int|float $price): bool; + + public function isNotLessThanOrEqualTo(PriceInterface|int|float $price): bool; + + public function isGreaterThan(PriceInterface|int|float $price): bool; + + public function isNotGreaterThan(PriceInterface|int|float $price): bool; + + public function isGreaterThanOrEqualTo(PriceInterface|int|float $price): bool; + + public function isNotGreaterThanOrEqualTo(PriceInterface|int|float $price): bool; + + public function isEqualTo(PriceInterface|int|float $price): bool; + + public function isNotEqualTo(PriceInterface|int|float $price): bool; + + public function isBetween(PriceInterface|int|float $first, PriceInterface|int|float $second): bool; + + public function isNotBetween(PriceInterface|int|float $first, PriceInterface|int|float $second): bool; + + public function isBetweenOrEqualTo(PriceInterface|int|float $first, PriceInterface|int|float $second): bool; + + public function isNotBetweenOrEqualTo(PriceInterface|int|float $first, PriceInterface|int|float $second): bool; + + public function isInteger(): bool; + + public function isNotInteger(): bool; + + public function isFloat(): bool; + + public function isNotFloat(): bool; + + public function isEven(): bool; + + public function isNotEven(): bool; + + public function isOdd(): bool; + + public function isNotOdd(): bool; + + public function hasSameValueAs(NumericalProperty|int|float $price): bool; + + public function hasNotSameValueAs(NumericalProperty|int|float $price): bool; + + public function hasSameValueWithVATAs(NumericalProperty|int|float $price): bool; + + public function hasNotSameValueWithVATAs(NumericalProperty|int|float $price): bool; + + public function isWithVATEqualTo(PriceInterface|int|float $price): bool; + + public function isWithVATNotEqualTo(PriceInterface|int|float $price): bool; + + public function is(PriceInterface $price, bool $strict = false): bool; + + public function isNot(PriceInterface $price, bool $strict = false): bool; +} diff --git a/src/Price.php b/src/Price.php index 6a80c84..6c2ff99 100644 --- a/src/Price.php +++ b/src/Price.php @@ -7,6 +7,7 @@ use DateTime; use MiBo\Prices\Calculators\PriceCalc; use MiBo\Prices\Contracts\PriceInterface; +use MiBo\Prices\Traits\PriceComparing; use MiBo\Prices\Traits\PriceHelper; use MiBo\Properties\Contracts\ComparableProperty; use MiBo\Properties\Contracts\NumericalComparableProperty; @@ -34,6 +35,7 @@ */ class Price extends NumericalProperty implements PriceInterface { + use PriceComparing; use PriceHelper; protected VAT $vat; @@ -243,501 +245,50 @@ public function getUnit(): Unit /** * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isLessThan(float|int|NumericalComparableProperty $property): bool - { - return parent::isLessThan($property); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isNotLessThan(float|int|NumericalComparableProperty $property): bool - { - return parent::isNotLessThan($property); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isLessThanOrEqualTo(float|int|NumericalComparableProperty $property): bool - { - return parent::isLessThanOrEqualTo($property); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isNotLessThanOrEqualTo(float|int|NumericalComparableProperty $property): bool - { - return parent::isNotLessThanOrEqualTo($property); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. */ - public function isGreaterThan(float|int|NumericalComparableProperty $property): bool + public function round(int $precision = 0, int $mode = PHP_ROUND_HALF_UP): static { - return parent::isGreaterThan($property); - } + $values = PriceCalc::round($this, $precision, $mode); - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isNotGreaterThan(float|int|NumericalComparableProperty $property): bool - { - return parent::isNotGreaterThan($property); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isGreaterThanOrEqualTo(float|int|NumericalComparableProperty $property): bool - { - return parent::isGreaterThanOrEqualTo($property); - } + $this->multiply(0); - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isNotGreaterThanOrEqualTo(float|int|NumericalComparableProperty $property): bool - { - return parent::isNotGreaterThanOrEqualTo($property); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isEqualTo(float|int|NumericalComparableProperty $property): bool - { - if ($property instanceof self) { - $property->forCountry($this->getVAT()->getCountryCode()); + foreach ($values as $category => $value) { + $this->prices[$category]->add($value); } - return parent::isEqualTo($property); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isNotEqualTo(float|int|NumericalComparableProperty $property): bool - { - return parent::isNotEqualTo($property); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isBetween( - float|int|NumericalComparableProperty $first, - float|int|NumericalComparableProperty $second - ): bool - { - return parent::isBetween($first, $second); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isNotBetween( - float|int|NumericalComparableProperty $first, - float|int|NumericalComparableProperty $second - ): bool - { - return parent::isNotBetween($first, $second); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isBetweenOrEqualTo( - float|int|NumericalComparableProperty $first, - float|int|NumericalComparableProperty $second - ): bool - { - return parent::isBetweenOrEqualTo($first, $second); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isNotBetweenOrEqualTo( - float|int|NumericalComparableProperty $first, - float|int|NumericalComparableProperty $second - ): bool - { - return parent::isNotBetweenOrEqualTo($first, $second); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isInteger(): bool - { - return parent::isInteger(); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isNotInteger(): bool - { - return parent::isNotInteger(); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isFloat(): bool - { - return parent::isFloat(); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isNotFloat(): bool - { - return parent::isNotFloat(); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isEven(): bool - { - return parent::isEven(); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isNotEven(): bool - { - return parent::isNotEven(); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isOdd(): bool - { - return parent::isOdd(); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isNotOdd(): bool - { - return parent::isNotOdd(); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function round(int $precision = 0, int $mode = PHP_ROUND_HALF_UP): static - { - return parent::round($precision, $mode); + return $this; } /** * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. */ public function ceil(int $precision = 0): static { - return parent::ceil($precision); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function floor(int $precision = 0): static - { - return parent::floor($precision); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function hasSameValueAs(float|int|NumericalComparableProperty $property): bool - { - return parent::hasSameValueAs($property); - } - - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function hasNotSameValueAs(float|int|NumericalComparableProperty $property): bool - { - return parent::hasNotSameValueAs($property); - } + $values = PriceCalc::ceil($this, $precision); - /** - * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function is(float|ComparableProperty|int $property, bool $strict = false): bool - { - if (!is_int($property) && !is_float($property) && !$property instanceof self) { - return false; - } + $this->multiply(0); - if ($strict && (is_int($property) || is_float($property))) { - return false; + foreach ($values as $category => $value) { + $this->prices[$category]->add($value); } - if ($strict && !$this->getUnit()->is($property->getUnit())) { - return false; - } - - if ($strict && !$this->getVAT()->is($property->getVAT())) { - return false; - } - - if ($property instanceof self) { - $property->convertToUnit($this->getUnit()); - $property->forCountry($this->getVAT()->getCountryCode()); - } - - return $this->hasSameValueAs($property) && $this->hasSameValueWithVATAs($property); + return $this; } /** * @inheritDoc - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isNot(float|ComparableProperty|int $property, bool $strict = false): bool - { - return parent::isNot($property, $strict); - } - - /** - * Checks that the value with VAT is same as the value of given property. - * - * @param \MiBo\Properties\Contracts\ComparableProperty|float|int $property - * - * @return bool - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. */ - public function hasSameValueWithVATAs(ComparableProperty|float|int $property): bool + public function floor(int $precision = 0): static { - if (is_int($property) || is_float($property)) { - return $this->getValueWithVAT() === $property || (float) $this->getValueWithVAT() === (float) $property; - } - - if (!$property instanceof self) { - return false; - } + $values = PriceCalc::floor($this, $precision); - return $this->getValueWithVAT() === $property->getValueWithVAT(); - } + $this->multiply(0); - /** - * Checks that the value with VAT is not same as the value of given property. - * - * @param \MiBo\Properties\Contracts\ComparableProperty|float|int $property - * - * @return bool - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function hasNotSameValueWithVATAs(ComparableProperty|float|int $property): bool - { - return !$this->hasSameValueWithVATAs($property); - } - - /** - * Checks that the value with VAT is same as the value of given property. - * - * **This method converts the property if not same unit and VAT!** - * - * @param \MiBo\Properties\Contracts\ComparableProperty|float|int $property - * - * @return bool - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isWithVATEqualTo(ComparableProperty|float|int $property): bool - { - if ($property instanceof self) { - $property->convertToUnit($this->getUnit()); - $property->forCountry($this->getVAT()->getCountryCode()); + foreach ($values as $category => $value) { + $this->prices[$category]->add($value); } - return $this->hasSameValueWithVATAs($property); - } - - /** - * Checks that the value with VAT is same as the value of given property. - * - * **This method converts the property if not same unit and VAT!** - * - * @param \MiBo\Properties\Contracts\ComparableProperty|float|int $property - * - * @return bool - * - * @experimental This method is in experimental phase and its result may change in the future. The only - * reason of that is that comparing prices with different VAT rates and currencies is not a trivial - * task. - * @deprecated See experimental note. - */ - public function isWithVATNotEqualTo(ComparableProperty|float|int $property): bool - { - return !$this->isWithVATEqualTo($property); + return $this; } // @codeCoverageIgnoreEnd diff --git a/src/Traits/PriceComparing.php b/src/Traits/PriceComparing.php new file mode 100644 index 0000000..7113b0a --- /dev/null +++ b/src/Traits/PriceComparing.php @@ -0,0 +1,375 @@ + + * + * @since 1.2 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise. + */ +trait PriceComparing +{ + /** + * @inheritDoc + */ + public function isLessThan(float|int|NumericalComparableProperty $property): bool + { + if ($property instanceof NumericalComparableProperty && !$property instanceof PriceInterface) { + throw new ValueError('Property must be instance of PriceInterface!'); + } + + return PriceCalc::checkThat($this)->isLessThan($property); + } + + /** + * @inheritDoc + */ + public function isNotLessThan(float|int|NumericalComparableProperty $property): bool + { + if ($property instanceof NumericalComparableProperty && !$property instanceof PriceInterface) { + throw new ValueError('Property must be instance of PriceInterface!'); + } + + return PriceCalc::checkThat($this)->isNotLessThan($property); + } + + /** + * @inheritDoc + */ + public function isLessThanOrEqualTo(float|int|NumericalComparableProperty $property): bool + { + if ($property instanceof NumericalComparableProperty && !$property instanceof PriceInterface) { + throw new ValueError('Property must be instance of PriceInterface!'); + } + + return PriceCalc::checkThat($this)->isLessThanOrEqualTo($property); + } + + /** + * @inheritDoc + */ + public function isNotLessThanOrEqualTo(float|int|NumericalComparableProperty $property): bool + { + if ($property instanceof NumericalComparableProperty && !$property instanceof PriceInterface) { + throw new ValueError('Property must be instance of PriceInterface!'); + } + + return PriceCalc::checkThat($this)->isNotLessThanOrEqualTo($property); + } + + /** + * @inheritDoc + */ + public function isGreaterThan(float|int|NumericalComparableProperty $property): bool + { + if ($property instanceof NumericalComparableProperty && !$property instanceof PriceInterface) { + throw new ValueError('Property must be instance of PriceInterface!'); + } + + return PriceCalc::checkThat($this)->isGreaterThan($property); + } + + /** + * @inheritDoc + */ + public function isNotGreaterThan(float|int|NumericalComparableProperty $property): bool + { + if ($property instanceof NumericalComparableProperty && !$property instanceof PriceInterface) { + throw new ValueError('Property must be instance of PriceInterface!'); + } + + return PriceCalc::checkThat($this)->isNotGreaterThan($property); + } + + /** + * @inheritDoc + */ + public function isGreaterThanOrEqualTo(float|int|NumericalComparableProperty $property): bool + { + if ($property instanceof NumericalComparableProperty && !$property instanceof PriceInterface) { + throw new ValueError('Property must be instance of PriceInterface!'); + } + + return PriceCalc::checkThat($this)->isGreaterThanOrEqualTo($property); + } + + /** + * @inheritDoc + */ + public function isNotGreaterThanOrEqualTo(float|int|NumericalComparableProperty $property): bool + { + if ($property instanceof NumericalComparableProperty && !$property instanceof PriceInterface) { + throw new ValueError('Property must be instance of PriceInterface!'); + } + + return PriceCalc::checkThat($this)->isNotGreaterThanOrEqualTo($property); + } + + /** + * @inheritDoc + */ + public function isEqualTo(float|int|NumericalComparableProperty $property): bool + { + if ($property instanceof NumericalComparableProperty && !$property instanceof PriceInterface) { + throw new ValueError('Property must be instance of PriceInterface!'); + } + + return PriceCalc::checkThat($this)->isEqualTo($property); + } + + /** + * @inheritDoc + */ + public function isNotEqualTo(float|int|NumericalComparableProperty $property): bool + { + if ($property instanceof NumericalComparableProperty && !$property instanceof PriceInterface) { + throw new ValueError('Property must be instance of PriceInterface!'); + } + + return PriceCalc::checkThat($this)->isNotEqualTo($property); + } + + /** + * @inheritDoc + */ + public function isBetween( + float|int|NumericalComparableProperty $first, + float|int|NumericalComparableProperty $second + ): bool + { + if (($first instanceof NumericalComparableProperty && !$first instanceof PriceInterface) + || ($second instanceof NumericalComparableProperty && !$second instanceof PriceInterface) + ) { + throw new ValueError('Property must be instance of PriceInterface!'); + } + + return PriceCalc::checkThat($this)->isBetween($first, $second); + } + + /** + * @inheritDoc + */ + public function isNotBetween( + float|int|NumericalComparableProperty $first, + float|int|NumericalComparableProperty $second + ): bool + { + if (($first instanceof NumericalComparableProperty && !$first instanceof PriceInterface) + || ($second instanceof NumericalComparableProperty && !$second instanceof PriceInterface) + ) { + throw new ValueError('Property must be instance of PriceInterface!'); + } + + return PriceCalc::checkThat($this)->isNotBetween($first, $second); + } + + /** + * @inheritDoc + */ + public function isBetweenOrEqualTo( + float|int|NumericalComparableProperty $first, + float|int|NumericalComparableProperty $second + ): bool + { + if (($first instanceof NumericalComparableProperty && !$first instanceof PriceInterface) + || ($second instanceof NumericalComparableProperty && !$second instanceof PriceInterface) + ) { + throw new ValueError('Property must be instance of PriceInterface!'); + } + + return PriceCalc::checkThat($this)->isBetweenOrEqualTo($first, $second); + } + + /** + * @inheritDoc + */ + public function isNotBetweenOrEqualTo( + float|int|NumericalComparableProperty $first, + float|int|NumericalComparableProperty $second + ): bool + { + if (($first instanceof NumericalComparableProperty && !$first instanceof PriceInterface) + || ($second instanceof NumericalComparableProperty && !$second instanceof PriceInterface) + ) { + throw new ValueError('Property must be instance of PriceInterface!'); + } + + return PriceCalc::checkThat($this)->isNotBetweenOrEqualTo($first, $second); + } + + /** + * @inheritDoc + */ + public function isInteger(): bool + { + return PriceCalc::checkThat($this)->isInteger(); + } + + /** + * @inheritDoc + */ + public function isNotInteger(): bool + { + return PriceCalc::checkThat($this)->isNotInteger(); + } + + /** + * @inheritDoc + */ + public function isFloat(): bool + { + return PriceCalc::checkThat($this)->isFloat(); + } + + /** + * @inheritDoc + */ + public function isNotFloat(): bool + { + return PriceCalc::checkThat($this)->isNotFloat(); + } + + /** + * @inheritDoc + */ + public function isEven(): bool + { + return PriceCalc::checkThat($this)->isEven(); + } + + /** + * @inheritDoc + */ + public function isNotEven(): bool + { + return PriceCalc::checkThat($this)->isNotEven(); + } + + /** + * @inheritDoc + */ + public function isOdd(): bool + { + return PriceCalc::checkThat($this)->isOdd(); + } + + /** + * @inheritDoc + */ + public function isNotOdd(): bool + { + return PriceCalc::checkThat($this)->isNotOdd(); + } + + /** + * @inheritDoc + * + * @phpcs:ignore Generic.Files.LineLength.TooLong + * @param (\MiBo\Properties\Contracts\NumericalComparableProperty&\MiBo\Properties\Contracts\NumericalProperty)|float|int $property + */ + public function hasSameValueAs(float|int|NumericalComparableProperty $property): bool + { + return PriceCalc::checkThat($this)->hasSameValueAs($property); + } + + /** + * @inheritDoc + * + * @phpcs:ignore Generic.Files.LineLength.TooLong + * @param (\MiBo\Properties\Contracts\NumericalComparableProperty&\MiBo\Properties\Contracts\NumericalProperty)|float|int $property + */ + public function hasNotSameValueAs(float|int|NumericalComparableProperty $property): bool + { + return PriceCalc::checkThat($this)->hasNotSameValueAs($property); + } + + /** + * @inheritDoc + */ + public function is(float|ComparableProperty|int $property, bool $strict = false): bool + { + if ($property instanceof ComparableProperty && !$property instanceof PriceInterface) { + throw new ValueError('Property must be instance of PriceInterface!'); + } + + return $property instanceof PriceInterface && PriceCalc::checkThat($this)->is($property, $strict); + } + + /** + * @inheritDoc + */ + public function isNot(float|ComparableProperty|int $property, bool $strict = false): bool + { + if ($property instanceof ComparableProperty && !$property instanceof PriceInterface) { + throw new ValueError('Property must be instance of PriceInterface!'); + } + + return $property instanceof PriceInterface && PriceCalc::checkThat($this)->isNot($property, $strict); + } + + /** + * Checks that the value with VAT is same as the value of given property. + * + * @param (\MiBo\Properties\Contracts\ComparableProperty&\MiBo\Properties\NumericalProperty)|float|int $property + * + * @return bool + */ + public function hasSameValueWithVATAs(ComparableProperty|float|int $property): bool + { + return PriceCalc::checkThat($this)->hasSameValueWithVATAs($property); + } + + /** + * Checks that the value with VAT is not same as the value of given property. + * + * @param (\MiBo\Properties\Contracts\ComparableProperty&\MiBo\Properties\NumericalProperty)|float|int $property + * + * @return bool + */ + public function hasNotSameValueWithVATAs(ComparableProperty|float|int $property): bool + { + return PriceCalc::checkThat($this)->hasNotSameValueWithVATAs($property); + } + + /** + * Checks that the value with VAT is same as the value of given property. + * + * **This method converts the property if not same unit and VAT!** + * + * @param \MiBo\Prices\Contracts\PriceInterface|float|int $property + * + * @return bool + */ + public function isWithVATEqualTo(PriceInterface|float|int $property): bool + { + return PriceCalc::checkThat($this)->isWithVATEqualTo($property); + } + + /** + * Checks that the value with VAT is same as the value of given property. + * + * **This method converts the property if not same unit and VAT!** + * + * @param \MiBo\Prices\Contracts\PriceInterface|float|int $property + * + * @return bool + */ + public function isWithVATNotEqualTo(PriceInterface|float|int $property): bool + { + return PriceCalc::checkThat($this)->isWithVATNotEqualTo($property); + } +} diff --git a/src/Units/Price/Currency.php b/src/Units/Price/Currency.php index 9a7eb5f..360238c 100644 --- a/src/Units/Price/Currency.php +++ b/src/Units/Price/Currency.php @@ -59,6 +59,11 @@ class Currency implements NumericalUnit, CurrencyInterface /** * @param \MiBo\Currencies\CurrencyInterface $currency + * + * This method is ignored out of coverage, because the methods is called only once and without a custom + * run configuration for tests, there is no guarantee that the method will be called while testing. + * + * @codeCoverageIgnore */ public function __construct(CurrencyInterface $currency) { diff --git a/tests/Core/ComparingFeat/CalculatorHelpersTest.php b/tests/Core/ComparingFeat/CalculatorHelpersTest.php new file mode 100644 index 0000000..7fc4571 --- /dev/null +++ b/tests/Core/ComparingFeat/CalculatorHelpersTest.php @@ -0,0 +1,61 @@ + + * + * @since 1.2 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise. + * + * @coversDefaultClass \MiBo\Prices\Calculators\PriceCalc + */ +class CalculatorHelpersTest extends TestCase +{ + /** + * @small + * + * @covers ::setCalculatorHelper + * @covers ::setComparerHelper + * @covers ::__callStatic + * + * @return void + */ + public function test(): void + { + $price = new Price(10, Currency::get('EUR')); + + try { + $price->isLessThan(10); + $this->fail('Failed! The ComparingFeat is set up already (Comparer)'); + } catch (\DomainException) {} + + try { + $price->ceil(); + $this->fail('Failed! The ComparingFeat is set up already (Rounder)'); + } catch (\DomainException) {} + + PriceCalc::setCalculatorHelper(new TestingRounder()); + PriceCalc::setComparerHelper(new TestingComparer()); + + $price->isLessThan(10); + $price->ceil(); + + $this->expectException(\BadMethodCallException::class); + PriceCalc::someRandomMethodThatDoesNotExist(); + } +} diff --git a/tests/Core/ComparingFeat/ComparingTest.php b/tests/Core/ComparingFeat/ComparingTest.php index bce45cc..f82ae15 100644 --- a/tests/Core/ComparingFeat/ComparingTest.php +++ b/tests/Core/ComparingFeat/ComparingTest.php @@ -4,15 +4,32 @@ namespace MiBo\Prices\Tests\ComparingFeat; +use MiBo\Prices\Calculators\PriceCalc; use MiBo\Prices\Price; +use MiBo\Prices\Tests\TestingComparer; use MiBo\Prices\Tests\VATResolver; use MiBo\Prices\Tests\ComparingStatusEnum; use MiBo\Prices\Units\Price\Currency; +use MiBo\Properties\Area; use MiBo\Properties\Calculators\UnitConvertor; +use MiBo\Properties\Contracts\NumericalUnit; +use MiBo\Properties\Length; +use MiBo\Properties\LuminousIntensity; +use MiBo\Properties\Mass; +use MiBo\Properties\NumericalProperty; +use MiBo\Properties\ThermodynamicTemperature; +use MiBo\Properties\Units\Area\SquareMeter; +use MiBo\Properties\Units\Length\CentiMeter; +use MiBo\Properties\Units\Length\Meter; +use MiBo\Properties\Units\Length\MilliMeter; +use MiBo\Properties\Units\LuminousIntensity\Candela; +use MiBo\Properties\Units\Mass\KiloGram; +use MiBo\Properties\Units\ThermodynamicTemperature\DegreeCelsius; use MiBo\VAT\Enums\VATRate; use MiBo\VAT\Resolvers\ProxyResolver; use MiBo\VAT\VAT; use PHPUnit\Framework\TestCase; +use ValueError; /** * Class ComparingTest @@ -29,6 +46,232 @@ */ class ComparingTest extends TestCase { + /** + * @small + * + * @covers ::hasSameValueAs + * @covers ::hasNotSameValueAs + * + * @param int|float $value + * @param string $propertyClassName + * @param \MiBo\Properties\Contracts\NumericalUnit $unit + * + * @return void + * + * @dataProvider provideSameValues + */ + public function testValueEquality(int|float $value, string $propertyClassName, NumericalUnit $unit): void + { + $price = new Price($value, Currency::get('EUR')); + $property = new $propertyClassName($value, $unit); + + $this->assertTrue($price->hasSameValueAs($property)); + $this->assertFalse($price->hasNotSameValueAs($property)); + } + + /** + * @small + * + * @covers ::isBetween + * @covers ::isNotBetween + * @covers ::isBetweenOrEqualTo + * @covers ::isNotBetweenOrEqualTo + * + * @param \MiBo\Prices\Price $price + * @param float|int $first + * @param float|int $second + * + * @return void + * + * @dataProvider provideBetweenData + */ + public function testBetween(Price $price, float|int $first, float|int $second): void + { + $this->assertTrue($price->isBetween($first, $second)); + $this->assertTrue($price->isBetweenOrEqualTo($first, $second)); + $this->assertFalse($price->isNotBetween($first, $second)); + $this->assertFalse($price->isNotBetweenOrEqualTo($first, $second)); + } + + /** + * @small + * + * @covers ::isEqualTo + * @covers ::isNotEqualTo + * @covers ::isLessThan + * @covers ::isNotLessThan + * @covers ::isLessThanOrEqualTo + * @covers ::isNotLessThanOrEqualTo + * @covers ::isGreaterThan + * @covers ::isNotGreaterThan + * @covers ::isGreaterThanOrEqualTo + * @covers ::isNotGreaterThanOrEqualTo + * @covers ::isBetween + * @covers ::isNotBetween + * @covers ::isBetweenOrEqualTo + * @covers ::isNotBetweenOrEqualTo + * @covers ::is + * @covers ::isNot + * + * @param \MiBo\Properties\NumericalProperty $property + * + * @return void + * + * @dataProvider provideIncompatibleData + */ + public function testIncompatibleData(NumericalProperty $property, string $method): void + { + $price = new Price(10, Currency::get('EUR')); + $methods = [ + 'isEqualTo', + 'isNotEqualTo', + 'isLessThan', + 'isNotLessThan', + 'isLessThanOrEqualTo', + 'isNotLessThanOrEqualTo', + 'isGreaterThan', + 'isNotGreaterThan', + 'isGreaterThanOrEqualTo', + 'isNotGreaterThanOrEqualTo', + 'isBetween', + 'isNotBetween', + 'isBetweenOrEqualTo', + 'isNotBetweenOrEqualTo', + ]; + + $this->expectException(ValueError::class); + in_array($method, ['is', 'isNot']) ? $price->$method($property) : $price->$method($property, $property); + } + + /** + * @small + * + * @covers ::isEven + * @covers ::isOdd + * @covers ::isNotEven + * @covers ::isNotOdd + * @covers ::isInteger + * @covers ::isNotInteger + * @covers ::isFloat + * @covers ::isNotFloat + * + * @param bool|null $isEven + * @param \MiBo\Prices\Price $price + * + * @return void + * + * @dataProvider provideEvens + */ + public function testEvenOrOdd(?bool $isEven, Price $price): void + { + if ($isEven === true) { + $this->assertTrue($price->isEven()); + $this->assertFalse($price->isOdd()); + $this->assertTrue($price->isNotOdd()); + $this->assertFalse($price->isNotEven()); + $this->assertTrue($price->isInteger()); + $this->assertFalse($price->isNotInteger()); + $this->assertFalse($price->isFloat()); + $this->assertTrue($price->isNotFloat()); + } else if ($isEven === false) { + $this->assertTrue($price->isOdd()); + $this->assertFalse($price->isEven()); + $this->assertTrue($price->isNotEven()); + $this->assertFalse($price->isNotOdd()); + $this->assertTrue($price->isInteger()); + $this->assertFalse($price->isNotInteger()); + $this->assertFalse($price->isFloat()); + $this->assertTrue($price->isNotFloat()); + } else { + $this->assertFalse($price->isEven()); + $this->assertFalse($price->isOdd()); + $this->assertTrue($price->isNotEven()); + $this->assertTrue($price->isNotOdd()); + $this->assertFalse($price->isInteger()); + $this->assertTrue($price->isNotInteger()); + $this->assertTrue($price->isFloat()); + $this->assertFalse($price->isNotFloat()); + } + } + + /** + * @small + * + * @covers ::isEqualTo + * @covers ::isNotEqualTo + * @covers ::isLessThan + * @covers ::isNotLessThan + * @covers ::isLessThanOrEqualTo + * @covers ::isNotLessThanOrEqualTo + * @covers ::isGreaterThan + * @covers ::isNotGreaterThan + * @covers ::isGreaterThanOrEqualTo + * @covers ::isNotGreaterThanOrEqualTo + * @covers ::is + * @covers ::isNot + * + * @param bool|null $isGreater + * @param \MiBo\Prices\Price $price + * @param array $values + * + * @return void + * + * @dataProvider provideDataForComparing + */ + public function testEquality( + ?bool $isGreater, + Price $price, + array $values + ): void + { + foreach ($values as $value) { + if ($isGreater === null) { + $this->assertTrue($price->isEqualTo($value)); + $this->assertFalse($price->isNotEqualTo($value)); + $this->assertTrue($price->isLessThanOrEqualTo($value)); + $this->assertFalse($price->isNotLessThanOrEqualTo($value)); + $this->assertTrue($price->isGreaterThanOrEqualTo($value)); + $this->assertFalse($price->isNotGreaterThanOrEqualTo($value)); + $this->assertTrue($price->isNotLessThan($value)); + $this->assertFalse($price->isLessThan($value)); + $this->assertFalse($price->isGreaterThan($value)); + $this->assertTrue($price->isNotGreaterThan($value)); + $this->assertTrue($price->isBetweenOrEqualTo($value, 0)); + $this->assertFalse($price->isNotBetweenOrEqualTo($value, 0)); + $this->assertFalse($price->isNot($value)); + $this->assertFalse($price->is($value)); + } else if ($isGreater === true) { + $this->assertTrue($price->isGreaterThan($value)); + $this->assertFalse($price->isNotGreaterThan($value)); + $this->assertTrue($price->isNotLessThan($value)); + $this->assertFalse($price->isLessThan($value)); + $this->assertTrue($price->isGreaterThanOrEqualTo($value)); + $this->assertFalse($price->isNotGreaterThanOrEqualTo($value)); + $this->assertTrue($price->isNotLessThanOrEqualTo($value)); + $this->assertFalse($price->isLessThanOrEqualTo($value)); + $this->assertTrue($price->isBetweenOrEqualTo($value, 0)); + $this->assertTrue($price->isNotEqualTo($value)); + $this->assertFalse($price->isEqualTo($value)); + $this->assertFalse($price->isNot($value)); + $this->assertFalse($price->is($value)); + } else if ($isGreater === false) { + $this->assertTrue($price->isLessThan($value)); + $this->assertFalse($price->isNotLessThan($value)); + $this->assertTrue($price->isNotGreaterThan($value)); + $this->assertFalse($price->isGreaterThan($value)); + $this->assertTrue($price->isLessThanOrEqualTo($value)); + $this->assertFalse($price->isNotLessThanOrEqualTo($value)); + $this->assertTrue($price->isNotGreaterThanOrEqualTo($value)); + $this->assertFalse($price->isGreaterThanOrEqualTo($value)); + $this->assertTrue($price->isBetweenOrEqualTo($value, 0)); + $this->assertTrue($price->isNotEqualTo($value)); + $this->assertFalse($price->isEqualTo($value)); + $this->assertFalse($price->isNot($value)); + $this->assertFalse($price->is($value)); + } + } + } + /** * @small * @@ -38,7 +281,6 @@ class ComparingTest extends TestCase * @covers ::isWithVATNotEqualTo * @covers ::hasSameValueWithVATAs * @covers ::hasNotSameValueWithVATAs - * @covers ::is * * @param array $same * @param \MiBo\Prices\Price $first @@ -48,7 +290,7 @@ class ComparingTest extends TestCase * * @dataProvider provideSamePrices */ - public function testEquality(array $same, Price $first, Price|float|int $second): void + public function testEquality2(array $same, Price $first, Price|float|int $second): void { $this->assertSame( in_array(ComparingStatusEnum::PRICE_CHANGED_EQUALS, $same), @@ -78,20 +320,20 @@ public function testEquality(array $same, Price $first, Price|float|int $second) ); $this->assertSame( - in_array(ComparingStatusEnum::IS_SAME, $same), + false, $first->is($second) ); $this->assertSame( - !in_array(ComparingStatusEnum::IS_SAME, $same), + false, $first->isNot($second) ); $this->assertSame( - in_array(ComparingStatusEnum::IS_SAME_STRICT, $same), + false, $first->is($second, true) ); $this->assertSame( - !in_array(ComparingStatusEnum::IS_SAME_STRICT, $same), + false, $first->isNot($second, true) ); } @@ -109,18 +351,20 @@ protected function setUp(): void UnitConvertor::$unitConvertors[\MiBo\Prices\Quantities\Price::class] = function(Price $price, Currency $unit) { if ($price->getUnit()->getName() === "Euro" && $unit->getName() === "Czech Koruna") { return $price->getNumericalValue()->multiply(25); - } elseif ($price->getUnit()->is($unit)) { + } else if ($price->getUnit()->is($unit)) { return $price->getNumericalValue(); } return $price->getNumericalValue()->divide(25); }; + + PriceCalc::setComparerHelper(new TestingComparer()); } public static function provideSamePrices(): array { return [ - 'Returning exactly same prices' => [ + 'Returning exactly same prices' => [ [ ComparingStatusEnum::PRICE_EQUALS, ComparingStatusEnum::PRICE_WITH_VAT_EQUALS, @@ -132,7 +376,7 @@ public static function provideSamePrices(): array new Price(10, Currency::get("CZK"), VAT::get("CZE", VATRate::STANDARD, "1")), new Price(10, Currency::get("CZK"), VAT::get("CZE", VATRate::STANDARD, "1")), ], - 'Returning same prices with different currency (same on conversion)' => [ + 'Returning same prices with different currency (same on conversion)' => [ [ ComparingStatusEnum::PRICE_SMALLER, ComparingStatusEnum::PRICE_WITH_VAT_EQUALS, @@ -144,7 +388,7 @@ public static function provideSamePrices(): array new Price(10, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), new Price(250, Currency::get("CZK"), VAT::get("CZE", VATRate::STANDARD, "1")), ], - 'Returning not the same prices' => [ + 'Returning not the same prices' => [ [ ComparingStatusEnum::PRICE_EQUALS, ComparingStatusEnum::PRICE_CHANGED_GREATER, @@ -152,7 +396,7 @@ public static function provideSamePrices(): array new Price(10, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), new Price(10, Currency::get("CZK"), VAT::get("CZE", VATRate::STANDARD, "1")), ], - 'Returning not the same prices in same currency' => [ + 'Returning not the same prices in same currency' => [ [ ComparingStatusEnum::PRICE_SMALLER, ComparingStatusEnum::PRICE_WITH_VAT_SMALLER, @@ -161,7 +405,7 @@ public static function provideSamePrices(): array new Price(10, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), new Price(12, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "2")), ], - 'Returning same prices with different product classification category' => [ + 'Returning same prices with different product classification category' => [ [ ComparingStatusEnum::PRICE_EQUALS, ComparingStatusEnum::PRICE_WITH_VAT_EQUALS, @@ -173,7 +417,7 @@ public static function provideSamePrices(): array new Price(10, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), new Price(10, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "2")), ], - 'Returning same prices with different product classification category and different currency' => [ + 'Returning same prices with different product classification category and different currency' => [ [ ComparingStatusEnum::PRICE_SMALLER, ComparingStatusEnum::PRICE_WITH_VAT_EQUALS, @@ -197,7 +441,7 @@ public static function provideSamePrices(): array new Price(10, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), new Price(250, Currency::get("CZK"), VAT::get("CZE", VATRate::STANDARD, "2")), ], - 'Returning same prices with different VAT rate' => [ + 'Returning same prices with different VAT rate' => [ [ ComparingStatusEnum::PRICE_EQUALS, ComparingStatusEnum::PRICE_WITH_VAT_EQUALS, @@ -209,7 +453,7 @@ public static function provideSamePrices(): array new Price(10, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), new Price(10, Currency::get("EUR"), VAT::get("CZE", VATRate::REDUCED, "1")), ], - 'Returning integer' => [ + 'Returning integer' => [ [ ComparingStatusEnum::PRICE_EQUALS, ComparingStatusEnum::PRICE_CHANGED_EQUALS, @@ -217,7 +461,7 @@ public static function provideSamePrices(): array new Price(10, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), 10, ], - 'Returning integer same value with vat' => [ + 'Returning integer same value with vat' => [ [ ComparingStatusEnum::PRICE_EQUALS, ComparingStatusEnum::PRICE_WITH_VAT_EQUALS, @@ -226,7 +470,7 @@ public static function provideSamePrices(): array new Price(10, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), 12.1, ], - 'Returning integer same value' => [ + 'Returning integer same value' => [ [ ComparingStatusEnum::PRICE_EQUALS, ComparingStatusEnum::PRICE_CHANGED_EQUALS, @@ -236,7 +480,229 @@ public static function provideSamePrices(): array ], new Price(10, Currency::get("EUR"), VAT::get("CZE", VATRate::NONE, "10")), 10, - ] + ], + ]; + } + + public static function provideDataForComparing(): array + { + return [ + 'Same' => [ + null, + new Price(10, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + [ + 10, + 10.0, + 10.00, + 10, + ], + ], + 'Less' => [ + true, + new Price(10, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + [ + 9, + 9.0, + 9.00, + 9, + 9.99, + 1, + -1, + 0, + 0.1, + ], + ], + 'Greater' => [ + false, + new Price(10, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + [ + 11, + 11.0, + 11.00, + 11, + 11.01, + 100, + 1000, + 10000, + 100000, + 1000000, + ], + ], + ]; + } + + public static function provideEvens(): array + { + return [ + [ + true, + new Price(10, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + ], + [ + true, + new Price(12, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + ], + [ + true, + new Price(0, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + ], + [ + true, + new Price(2, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + ], + [ + true, + new Price(100, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + ], + [ + true, + new Price(60, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + ], + [ + null, + new Price(50.2, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + ], + [ + false, + new Price(1, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + ], + [ + false, + new Price(3, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + ], + [ + false, + new Price(15, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + ], + [ + false, + new Price(9, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + ], + [ + false, + new Price(-1, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + ], + [ + false, + new Price(1, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + ], + [ + false, + new Price(01, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + ], + [ + null, + new Price(10.1, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + ], + [ + null, + new Price(10.2, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + ], + [ + null, + new Price(-0.04, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + ], + [ + true, + new Price(-10, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + ], + [ + true, + new Price(-2, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + ], + ]; + } + + public static function provideIncompatibleData(): \Generator + { + $methods = [ + 'isEqualTo', + 'isNotEqualTo', + 'isLessThan', + 'isNotLessThan', + 'isLessThanOrEqualTo', + 'isNotLessThanOrEqualTo', + 'isGreaterThan', + 'isNotGreaterThan', + 'isGreaterThanOrEqualTo', + 'isNotGreaterThanOrEqualTo', + 'isBetween', + 'isNotBetween', + 'isBetweenOrEqualTo', + 'isNotBetweenOrEqualTo', + 'is', + 'isNot', + ]; + + $data = [ + new Length(10, Meter::get()), + new Mass(10, Kilogram::get()), + new ThermodynamicTemperature(10, DegreeCelsius::get()), + new LuminousIntensity(10, Candela::get()), + ]; + + /** @var \MiBo\Properties\Contracts\NumericalProperty $property */ + foreach ($data as $property) { + $message = 'Testing incompatible type ' . $property->getQuantity()::getNameForTranslation(); + + foreach ($methods as $method) { + yield $message . ' method ' . $method => [ + $property, + $method, + ]; + } + } + } + + public static function provideBetweenData(): array + { + return [ + [ + new Price(10, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + 9, + 11, + ], + [ + new Price(10, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + 9.9, + 11, + ], + [ + new Price(10, Currency::get("EUR"), VAT::get("CZE", VATRate::STANDARD, "1")), + -1, + 10.1, + ], + ]; + } + + public static function provideSameValues(): array + { + return [ + [ + 10, + Length::class, + Meter::get(), + ], + [ + 10, + Length::class, + MilliMeter::get(), + ], + [ + 5, + Length::class, + CentiMeter::get(), + ], + [ + 1.1, + Length::class, + Meter::get(), + ], + [ + 10, + Area::class, + SquareMeter::get(), + ], ]; } } diff --git a/tests/TestingComparer.php b/tests/TestingComparer.php new file mode 100644 index 0000000..6522f45 --- /dev/null +++ b/tests/TestingComparer.php @@ -0,0 +1,215 @@ + + * + * @since 1.2 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise. + */ +class TestingComparer implements PriceComparer +{ + private PriceInterface $price; + + /** + * @return \MiBo\Prices\Contracts\PriceInterface + */ + private function getPrice(): PriceInterface + { + return $this->price; + } + + public function checkThat(PriceInterface $price): static + { + $this->price = $price; + + return $this; + } + + private function priceToValue(PriceInterface|float|int $price): float|int + { + if ($price instanceof PriceInterface) { + return $price->convertToUnit($this->getPrice()->getUnit())->getValue(); + } + + return $price; + } + + public function isLessThan(float|int|PriceInterface $price): bool + { + return $this->price->getValue() < $this->priceToValue($price); + } + + public function isNotLessThan(float|int|PriceInterface $price): bool + { + return !$this->isLessThan($price); + } + + public function isLessThanOrEqualTo(float|int|PriceInterface $price): bool + { + return $this->isLessThan($price) || $this->isEqualTo($price); + } + + public function isNotLessThanOrEqualTo(float|int|PriceInterface $price): bool + { + return !$this->isLessThanOrEqualTo($price); + } + + public function isGreaterThan(float|int|PriceInterface $price): bool + { + return $this->isNotLessThanOrEqualTo($price); + } + + public function isNotGreaterThan(float|int|PriceInterface $price): bool + { + return $this->isLessThanOrEqualTo($price); + } + + public function isGreaterThanOrEqualTo(float|int|PriceInterface $price): bool + { + return $this->isNotLessThan($price); + } + + public function isNotGreaterThanOrEqualTo(float|int|PriceInterface $price): bool + { + return $this->isLessThan($price); + } + + public function isEqualTo(float|int|PriceInterface $price): bool + { + return (float) $this->price->getValue() === (float) $this->priceToValue($price); + } + + public function isNotEqualTo(float|int|PriceInterface $price): bool + { + return !$this->isEqualTo($price); + } + + public function isBetween(float|int|PriceInterface $first, float|int|PriceInterface $second): bool + { + return ($this->isGreaterThan($first) && $this->isLessThan($second)) || + $this->isLessThan($first) || $this->isGreaterThan($second); + } + + public function isNotBetween(float|int|PriceInterface $first, float|int|PriceInterface $second): bool + { + return !$this->isBetween($first, $second); + } + + public function isBetweenOrEqualTo( + float|int|PriceInterface $first, + float|int|PriceInterface $second + ): bool + { + return $this->isBetween($first, $second) || $this->isEqualTo($first) || $this->isEqualTo($second); + } + + public function isNotBetweenOrEqualTo( + float|int|PriceInterface $first, + float|int|PriceInterface $second + ): bool + { + return !$this->isBetweenOrEqualTo($first, $second); + } + + public function isInteger(): bool + { + return $this->price->getValue() === (int) $this->price->getValue(); + } + + public function isNotInteger(): bool + { + return !$this->isInteger(); + } + + public function isFloat(): bool + { + return $this->price->getValue() === (float) $this->price->getValue(); + } + + public function isNotFloat(): bool + { + return !$this->isFloat(); + } + + public function isEven(): bool + { + return $this->isInteger() && $this->price->getValue() % 2 === 0; + } + + public function isNotEven(): bool + { + return !$this->isEven(); + } + + public function isOdd(): bool + { + return $this->isInteger() && $this->price->getValue() % 2 !== 0; + } + + public function isNotOdd(): bool + { + return !$this->isOdd(); + } + + public function hasSameValueAs(NumericalProperty|float|int $price): bool + { + return $this->price->getValue() === ($price instanceof NumericalProperty ? $price->getValue() : $price); + } + + public function hasNotSameValueAs(NumericalProperty|float|int $price): bool + { + return !$this->hasSameValueAs($price); + } + + public function hasSameValueWithVATAs(NumericalProperty|float|int $price): bool + { + $price = $price instanceof PriceInterface ? + $price->convertToUnit($this->getPrice()->getUnit())->getValueWithVAT() : + $price; + + return $this->price->getValueWithVAT() === ($price instanceof NumericalProperty ? + $price->getValue() : + $price + ); + } + + public function hasNotSameValueWithVATAs(NumericalProperty|float|int $price): bool + { + return !$this->hasSameValueWithVATAs($price); + } + + public function isWithVATEqualTo(float|int|PriceInterface $price): bool + { + return $this->price->getValueWithVAT() === ($price instanceof PriceInterface ? + $price->convertToUnit($this->getPrice()->getUnit())->getValueWithVAT() : + $price); + } + + public function isWithVATNotEqualTo(float|int|PriceInterface $price): bool + { + return !$this->isWithVATEqualTo($price); + } + + public function is(PriceInterface $price, bool $strict = false): bool + { + return false; + } + + public function isNot(PriceInterface $price, bool $strict = false): bool + { + return false; + } +} diff --git a/tests/TestingRounder.php b/tests/TestingRounder.php new file mode 100644 index 0000000..32d24ec --- /dev/null +++ b/tests/TestingRounder.php @@ -0,0 +1,37 @@ + + * + * @since 1.2 + * + * @no-named-arguments Parameter names are not covered by the backward compatibility promise. + */ +class TestingRounder implements PriceCalculatorHelper +{ + public function round(PriceInterface $price, int $precision = 0, int $mode = PHP_ROUND_HALF_UP): array + { + return [$price->getVAT()->getCategory() ?? '' => round($price->getValue(), $precision, $mode)]; + } + + public function ceil(PriceInterface $price, int $precision = 0): array + { + return [$price->getVAT()->getCategory() ?? '' => ceil($price->getValue())]; + } + + public function floor(PriceInterface $price, int $precision = 0): array + { + return [$price->getVAT()->getCategory() ?? '' => floor($price->getValue())]; + } +}