FleetCart/Modules/Support/Money.php
2023-06-11 13:14:03 +01:00

221 lines
5.3 KiB
PHP

<?php
namespace Modules\Support;
use NumberFormatter;
use JsonSerializable;
use InvalidArgumentException;
use Modules\Currency\Currency;
use Modules\Currency\Entities\CurrencyRate;
class Money implements JsonSerializable
{
private $amount;
private $currency;
public function __construct($amount, $currency)
{
$this->amount = $amount;
$this->currency = $currency;
}
public static function inDefaultCurrency($amount)
{
return new self($amount, setting('default_currency'));
}
public static function inCurrentCurrency($amount)
{
return new self($amount, currency());
}
private function newInstance($amount)
{
return new self($amount, $this->currency);
}
public function amount()
{
return $this->amount;
}
public function subunit()
{
$fraction = 10 ** Currency::subunit($this->currency);
return (int) round($this->amount * $fraction);
}
public function currency()
{
return $this->currency;
}
public function isZero()
{
return $this->amount == 0;
}
public function add($addend)
{
$addend = $this->convertToSameCurrency($addend);
return $this->newInstance($this->amount + $addend->amount);
}
public function subtract($subtrahend)
{
$subtrahend = $this->convertToSameCurrency($subtrahend);
return $this->newInstance($this->amount - $subtrahend->amount);
}
public function multiply($multiplier)
{
return $this->newInstance($this->amount * $multiplier);
}
public function divide($divisor)
{
return $this->newInstance($this->amount / $divisor);
}
public function lessThan($other)
{
return $this->amount < $other->amount;
}
public function lessThanOrEqual($other)
{
return $this->amount <= $other->amount;
}
public function greaterThan($other)
{
return $this->amount > $other->amount;
}
public function greaterThanOrEqual($other)
{
return $this->amount >= $other->amount;
}
private function convertToSameCurrency($other)
{
if ($this->isNotSameCurrency($other)) {
$other = $other->convertToDefaultCurrency();
}
$this->assertSameCurrency($other);
return $other;
}
public function isSameCurrency($other)
{
return $this->currency === $other->currency;
}
public function isNotSameCurrency($other)
{
return ! $this->isSameCurrency($other);
}
private function assertSameCurrency($other)
{
if ($this->isNotSameCurrency($other)) {
throw new InvalidArgumentException('Mismatch money currency.');
}
}
public function convertToDefaultCurrency()
{
$currencyRate = CurrencyRate::for($this->currency);
if (is_null($currencyRate)) {
throw new InvalidArgumentException('Cannot convert the money to the default currency.');
}
return new self($this->amount / $currencyRate, setting('default_currency'));
}
public function convertToCurrentCurrency($currencyRate = null)
{
return $this->convert(currency(), $currencyRate);
}
public function convert($currency, $currencyRate = null)
{
$currencyRate = $currencyRate ?: CurrencyRate::for($currency);
if (is_null($currencyRate)) {
throw new InvalidArgumentException("Cannot convert the money to currency [$currency].");
}
return new self($this->amount * $currencyRate, $currency);
}
public function round($precision = null, $mode = null)
{
if (is_null($precision)) {
$precision = Currency::subunit($this->currency);
}
$amount = round($this->amount, $precision, $mode);
return $this->newInstance($amount);
}
public function ceil()
{
return $this->newInstance(ceil($this->amount));
}
public function floor()
{
return $this->newInstance(floor($this->amount));
}
public function format($currency = null, $locale = null)
{
$currency = $currency ?: currency();
$locale = $locale ?: locale();
$numberFormatter = new NumberFormatter($locale, NumberFormatter::CURRENCY);
$amount = $numberFormatter->formatCurrency($this->amount, $currency);
/**
* Fix: Hungarian Forint outputs wrong currency format.
*
* @see https://github.com/MehediDracula/FleetCart/issues/18
*/
if (currency() === 'HUF') {
$amount = str_replace(',00', '', $amount);
}
return $amount;
}
public function toArray()
{
return [
'amount' => $this->amount,
'formatted' => $this->format(),
'currency' => $this->currency,
];
}
public function jsonSerialize()
{
return array_merge($this->toArray(), [
'inCurrentCurrency' => $this->convertToCurrentCurrency()->toArray(),
]);
}
public function __toString()
{
return (string) $this->amount;
}
}