mirror of
https://github.com/archtechx/money.git
synced 2025-12-13 03:34:04 +00:00
Initial commit
This commit is contained in:
commit
8847454577
33 changed files with 2435 additions and 0 deletions
45
src/Concerns/PersistsCurrency.php
Normal file
45
src/Concerns/PersistsCurrency.php
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ArchTech\Money\Concerns;
|
||||
|
||||
use Closure;
|
||||
|
||||
trait PersistsCurrency
|
||||
{
|
||||
protected Closure $resolveCurrentUsing;
|
||||
protected Closure $storeCurrentUsing;
|
||||
|
||||
protected function resolveCurrent(): string|null
|
||||
{
|
||||
return isset($this->resolveCurrentUsing)
|
||||
? ($this->resolveCurrentUsing)()
|
||||
: null;
|
||||
}
|
||||
|
||||
/** Set the handler for resolving the current currency. */
|
||||
public function resolveCurrentUsing(Closure $callback): static
|
||||
{
|
||||
$this->resolveCurrentUsing = $callback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function storeCurrent(string $currency): static
|
||||
{
|
||||
if (isset($this->storeCurrentUsing)) {
|
||||
($this->storeCurrentUsing)($currency);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** Set the handler for storing the current currency. */
|
||||
public function storeCurrentUsing(Closure $callback): static
|
||||
{
|
||||
$this->storeCurrentUsing = $callback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
138
src/Concerns/RegistersCurrencies.php
Normal file
138
src/Concerns/RegistersCurrencies.php
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ArchTech\Money\Concerns;
|
||||
|
||||
use ArchTech\Money\Currency;
|
||||
use ArchTech\Money\Exceptions\CurrencyDoesNotExistException;
|
||||
use ArchTech\Money\Exceptions\InvalidCurrencyException;
|
||||
use Closure;
|
||||
use ReflectionClass;
|
||||
|
||||
trait RegistersCurrencies
|
||||
{
|
||||
/**
|
||||
* Registered currencies.
|
||||
*
|
||||
* @var array<string, Currency>
|
||||
*/
|
||||
protected array $currencies = [];
|
||||
|
||||
/** Register a currency. */
|
||||
public function add(string|Currency|Closure|array $currencies): static
|
||||
{
|
||||
// $currencies can be:
|
||||
// new Currency(...)
|
||||
// [new Currency(..), new Currency(...)]
|
||||
// USD::class
|
||||
// new USD
|
||||
// ['code' => 'GBP', 'rate' => 0.8, 'name' => 'British Pound']
|
||||
// Or a Closure returning any of the above
|
||||
|
||||
// Invoke Closures
|
||||
$currencies = value($currencies);
|
||||
|
||||
// Make sure we're working with an array
|
||||
$currencies = is_array($currencies) ? $currencies : [$currencies];
|
||||
|
||||
// If we're working with a single currency as an
|
||||
// array, we'll manually wrap it again in [].
|
||||
if (isset($currencies['code'])) {
|
||||
$currencies = [$currencies];
|
||||
}
|
||||
|
||||
foreach ($currencies as $currency) {
|
||||
// ['code' => 'GBP', 'rate' => 0.8, 'name' => 'British Pound']
|
||||
if (is_array($currency)) {
|
||||
$currency = Currency::fromArray($currency);
|
||||
}
|
||||
|
||||
// USD::class
|
||||
if (is_string($currency)) {
|
||||
$currency = new $currency;
|
||||
}
|
||||
|
||||
/** @var Currency $currency */
|
||||
$this->currencies[$currency->code()] = $currency;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** Unregister a currency. */
|
||||
public function remove(string $currency): static
|
||||
{
|
||||
$code = $this->getCode($currency);
|
||||
|
||||
if ($this->has($code)) {
|
||||
unset($this->currencies[$code]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** List all registered currencies */
|
||||
public function all(): array
|
||||
{
|
||||
return $this->currencies;
|
||||
}
|
||||
|
||||
/** Unregister all currencies. */
|
||||
public function clear(): static
|
||||
{
|
||||
$this->currencies = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** Fetch a currency by its code. */
|
||||
public function get(string $currency): Currency
|
||||
{
|
||||
// Converting this to the code in case a class string is passed
|
||||
$code = $this->getCode($currency);
|
||||
|
||||
$this->ensureCurrencyExists($code);
|
||||
|
||||
return $this->currencies[$code];
|
||||
}
|
||||
|
||||
/** Check if a currency is registered. */
|
||||
public function has(string $currency): bool
|
||||
{
|
||||
// Converting this to the code in case a class string is passed
|
||||
$code = $this->getCode($currency);
|
||||
|
||||
return isset($this->currencies[$code]);
|
||||
}
|
||||
|
||||
/** Abort execution if a currency doesn't exist. */
|
||||
public function ensureCurrencyExists(string $currency): static
|
||||
{
|
||||
if (! $this->has($currency)) {
|
||||
throw new CurrencyDoesNotExistException($currency);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** Get a currency's code. */
|
||||
public function getCode(Currency|string $currency): string
|
||||
{
|
||||
if (is_string($currency) && isset($this->currencies[$currency])) {
|
||||
return $currency;
|
||||
}
|
||||
|
||||
if ($currency instanceof Currency) {
|
||||
return $currency->code();
|
||||
}
|
||||
|
||||
if (class_exists($currency) && (new ReflectionClass($currency))->isSubclassOf(Currency::class)) {
|
||||
return (new $currency)->code();
|
||||
}
|
||||
|
||||
throw new InvalidCurrencyException(
|
||||
"{$currency} is not a valid currency.",
|
||||
);
|
||||
}
|
||||
}
|
||||
18
src/Currencies/USD.php
Normal file
18
src/Currencies/USD.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ArchTech\Money\Currencies;
|
||||
|
||||
use ArchTech\Money\Currency;
|
||||
|
||||
class USD extends Currency
|
||||
{
|
||||
protected string $code = 'USD';
|
||||
protected string $name = 'United States Dollar';
|
||||
protected float $rate = 1.0;
|
||||
protected int $mathDecimals = 2;
|
||||
protected int $displayDecimals = 2;
|
||||
protected int $rounding = 2;
|
||||
protected string $prefix = '$';
|
||||
}
|
||||
186
src/Currency.php
Normal file
186
src/Currency.php
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ArchTech\Money;
|
||||
|
||||
use ArchTech\Money\Exceptions\InvalidCurrencyException;
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
use JsonSerializable;
|
||||
|
||||
class Currency implements Arrayable, JsonSerializable
|
||||
{
|
||||
/** Code of the currency (e.g. 'CZK'). */
|
||||
protected string $code;
|
||||
|
||||
/** Name of the currency (e.g. 'Czech Crown'). */
|
||||
protected string $name;
|
||||
|
||||
/** Rate of this currency relative to the default currency. */
|
||||
protected float $rate;
|
||||
|
||||
/** Prefix placed at the beginning of the formatted value. */
|
||||
protected string $prefix;
|
||||
|
||||
/** Suffix placed at the end of the formatted value. */
|
||||
protected string $suffix;
|
||||
|
||||
/** Number of decimals used in money calculations. */
|
||||
protected int $mathDecimals;
|
||||
|
||||
/** Number of decimals used in the formatted value. */
|
||||
protected int $displayDecimals;
|
||||
|
||||
/** The character used to separate the decimal values. */
|
||||
protected string $decimalSeparator;
|
||||
|
||||
/** The character used to separate groups of thousands. */
|
||||
protected string $thousandsSeparator;
|
||||
|
||||
/** How many decimals of the currency's values should get rounded. */
|
||||
protected int $rounding;
|
||||
|
||||
/** Create a new Currency instance. */
|
||||
public function __construct(
|
||||
string $code = null,
|
||||
string $name = null,
|
||||
float $rate = null,
|
||||
string $prefix = null,
|
||||
string $suffix = null,
|
||||
int $mathDecimals = null,
|
||||
int $displayDecimals = null,
|
||||
int $rounding = null,
|
||||
string $decimalSeparator = null,
|
||||
string $thousandsSeparator = null,
|
||||
) {
|
||||
$this->code = $code ?? $this->code ?? '';
|
||||
$this->name = $name ?? $this->name ?? '';
|
||||
$this->rate = $rate ?? $this->rate ?? 1;
|
||||
$this->prefix = $prefix ?? $this->prefix ?? '';
|
||||
$this->suffix = $suffix ?? $this->suffix ?? '';
|
||||
$this->mathDecimals = $mathDecimals ?? $this->mathDecimals ?? 2;
|
||||
$this->displayDecimals = $displayDecimals ?? $this->displayDecimals ?? 2;
|
||||
$this->decimalSeparator = $decimalSeparator ?? $this->decimalSeparator ?? '.';
|
||||
$this->thousandsSeparator = $thousandsSeparator ?? $this->thousandsSeparator ?? ',';
|
||||
$this->rounding = $rounding ?? $this->rounding ?? $this->mathDecimals;
|
||||
|
||||
$this->check();
|
||||
}
|
||||
|
||||
/** Create an anonymous Currency instance from an array. */
|
||||
public static function fromArray(array $currency): static
|
||||
{
|
||||
return new static(...$currency);
|
||||
}
|
||||
|
||||
/** Get the currency's code. */
|
||||
public function code(): string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
/** Get the currency's name. */
|
||||
public function name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/** Get the currency's rate. */
|
||||
public function rate(): float
|
||||
{
|
||||
return $this->rate;
|
||||
}
|
||||
|
||||
/** Get the currency's prefix. */
|
||||
public function prefix(): string
|
||||
{
|
||||
return $this->prefix;
|
||||
}
|
||||
|
||||
/** Get the currency's suffix. */
|
||||
public function suffix(): string
|
||||
{
|
||||
return $this->suffix;
|
||||
}
|
||||
|
||||
/** Get the currency's math decimal count. */
|
||||
public function mathDecimals(): int
|
||||
{
|
||||
return $this->mathDecimals;
|
||||
}
|
||||
|
||||
/** Get the currency's math decimal count. */
|
||||
public function displayDecimals(): int
|
||||
{
|
||||
return $this->displayDecimals;
|
||||
}
|
||||
|
||||
/** Get the currency's decimal separator. */
|
||||
public function decimalSeparator(): string
|
||||
{
|
||||
return $this->decimalSeparator;
|
||||
}
|
||||
|
||||
/** Get the currency's thousands separator. */
|
||||
public function thousandsSeparator(): string
|
||||
{
|
||||
return $this->thousandsSeparator;
|
||||
}
|
||||
|
||||
/** Get the currency's rounding. */
|
||||
public function rounding(): int
|
||||
{
|
||||
return $this->rounding;
|
||||
}
|
||||
|
||||
/** Convert the currency to a string (returns the code). */
|
||||
public function __toString()
|
||||
{
|
||||
return $this->code();
|
||||
}
|
||||
|
||||
/** Convert the currency to an array. */
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'code' => $this->code,
|
||||
'name' => $this->name,
|
||||
'rate' => $this->rate,
|
||||
'prefix' => $this->prefix,
|
||||
'suffix' => $this->suffix,
|
||||
'mathDecimals' => $this->mathDecimals,
|
||||
'displayDecimals' => $this->displayDecimals,
|
||||
'rounding' => $this->rounding,
|
||||
'decimalSeparator' => $this->decimalSeparator,
|
||||
'thousandsSeparator' => $this->thousandsSeparator,
|
||||
];
|
||||
}
|
||||
|
||||
/** Get the data used for JSON serialization. */
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/** Create a currency from JSON. */
|
||||
public static function fromJson(string|array $json): self
|
||||
{
|
||||
if (is_string($json)) {
|
||||
$json = json_decode($json, true, flags: JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
return static::fromArray($json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the currency has all required values.
|
||||
*
|
||||
* @throws InvalidCurrencyException
|
||||
*/
|
||||
protected function check(): void
|
||||
{
|
||||
if (! $this->code() || ! $this->name()) {
|
||||
throw new InvalidCurrencyException('This currency does not have a code or a name.');
|
||||
}
|
||||
}
|
||||
}
|
||||
82
src/CurrencyManager.php
Normal file
82
src/CurrencyManager.php
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ArchTech\Money;
|
||||
|
||||
use ArchTech\Money\Concerns\PersistsCurrency;
|
||||
use ArchTech\Money\Concerns\RegistersCurrencies;
|
||||
use ArchTech\Money\Currencies\USD;
|
||||
|
||||
class CurrencyManager
|
||||
{
|
||||
use RegistersCurrencies, PersistsCurrency;
|
||||
|
||||
/** The default currency's code. */
|
||||
protected string $default = 'USD';
|
||||
|
||||
/** The current currency's code. */
|
||||
protected string $current;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/** Reset the object to the default state. */
|
||||
public function reset(): static
|
||||
{
|
||||
$this->currencies = [
|
||||
'USD' => new USD,
|
||||
];
|
||||
|
||||
$this->default = 'USD';
|
||||
|
||||
$this->forgetCurrent();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function forgetCurrent(): static
|
||||
{
|
||||
unset($this->current);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** Get the default currency. */
|
||||
public function getDefault(): Currency
|
||||
{
|
||||
return $this->get($this->default);
|
||||
}
|
||||
|
||||
/** Set the default currency. */
|
||||
public function setDefault(string $currency): static
|
||||
{
|
||||
$code = $this->getCode($currency);
|
||||
|
||||
$this->ensureCurrencyExists($code);
|
||||
|
||||
$this->default = $code;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** Get the current currency. */
|
||||
public function getCurrent(): Currency
|
||||
{
|
||||
return $this->get($this->current ??= $this->resolveCurrent() ?? $this->default);
|
||||
}
|
||||
|
||||
/** Set the current currency. */
|
||||
public function setCurrent(Currency|string $currency): static
|
||||
{
|
||||
$code = $this->getCode($currency);
|
||||
|
||||
$this->ensureCurrencyExists($code);
|
||||
|
||||
$this->storeCurrent($this->current = $code);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
15
src/Exceptions/CurrencyDoesNotExistException.php
Normal file
15
src/Exceptions/CurrencyDoesNotExistException.php
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ArchTech\Money\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class CurrencyDoesNotExistException extends Exception
|
||||
{
|
||||
public function __construct(string $code)
|
||||
{
|
||||
parent::__construct("The $code currency does not exist.");
|
||||
}
|
||||
}
|
||||
15
src/Exceptions/InvalidCurrencyException.php
Normal file
15
src/Exceptions/InvalidCurrencyException.php
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ArchTech\Money\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class InvalidCurrencyException extends Exception
|
||||
{
|
||||
public function __construct(string $message = null)
|
||||
{
|
||||
parent::__construct($message ?? 'The currency is invalid');
|
||||
}
|
||||
}
|
||||
261
src/Money.php
Normal file
261
src/Money.php
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ArchTech\Money;
|
||||
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
use JsonSerializable;
|
||||
use Livewire\Wireable;
|
||||
|
||||
final class Money implements JsonSerializable, Arrayable, Wireable
|
||||
{
|
||||
protected int $value;
|
||||
protected Currency $currency;
|
||||
|
||||
/** Create a new Money instance. */
|
||||
public function __construct(int $value, Currency|string $currency = null)
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->currency = currency($currency);
|
||||
}
|
||||
|
||||
/** Create a new Money instance with the same currency. */
|
||||
protected function new(int $value): self
|
||||
{
|
||||
return new self($value, $this->currency);
|
||||
}
|
||||
|
||||
/** Create a Money instance from a decimal value. */
|
||||
public static function fromDecimal(float $decimal, Currency|string $currency = null): self
|
||||
{
|
||||
return new static(
|
||||
(int) round($decimal * 10 ** currency($currency)->mathDecimals()),
|
||||
currency($currency)
|
||||
);
|
||||
}
|
||||
|
||||
/** Add money (in base value). */
|
||||
public function add(int $value): self
|
||||
{
|
||||
return $this->new($this->value + $value);
|
||||
}
|
||||
|
||||
/** Add money (from another Money instance). */
|
||||
public function addMoney(self $money): self
|
||||
{
|
||||
return $this->add(
|
||||
$money->convertTo($this->currency)->value()
|
||||
);
|
||||
}
|
||||
|
||||
/** Subtract money (in base value). */
|
||||
public function subtract(int $value): self
|
||||
{
|
||||
return $this->new($this->value - $value);
|
||||
}
|
||||
|
||||
/** Subtract money (of another Money instance). */
|
||||
public function subtractMoney(self $money): self
|
||||
{
|
||||
return $this->subtract(
|
||||
$money->convertTo($this->currency)->value()
|
||||
);
|
||||
}
|
||||
|
||||
/** Multiply the money by a coefficient. */
|
||||
public function multiplyBy(float $coefficient): self
|
||||
{
|
||||
return $this->new(
|
||||
(int) round($this->value * $coefficient)
|
||||
);
|
||||
}
|
||||
|
||||
/** Multiply the money by a coefficient. */
|
||||
public function times(float $coefficient): self
|
||||
{
|
||||
return $this->multiplyBy($coefficient);
|
||||
}
|
||||
|
||||
/** Divide the money by a number. */
|
||||
public function divideBy(float $number): self
|
||||
{
|
||||
if ($number == 0) {
|
||||
$number = 1;
|
||||
}
|
||||
|
||||
return $this->new(
|
||||
(int) round($this->value() / $number)
|
||||
);
|
||||
}
|
||||
|
||||
/** Add a % fee to the money. */
|
||||
public function addFee(float $rate): self
|
||||
{
|
||||
return $this->multiplyBy(
|
||||
round(1 + $rate, $this->currency->mathDecimals())
|
||||
);
|
||||
}
|
||||
|
||||
/** Add a % tax to the money. */
|
||||
public function addTax(float $rate): self
|
||||
{
|
||||
return $this->addFee($rate);
|
||||
}
|
||||
|
||||
/** Subtract a % fee from the money. */
|
||||
public function subtractFee(float $rate): self
|
||||
{
|
||||
return $this->divideBy(
|
||||
round(1 + $rate, $this->currency->mathDecimals())
|
||||
);
|
||||
}
|
||||
|
||||
/** Subtract a % tax from the money. */
|
||||
public function subtractTax(float $rate): self
|
||||
{
|
||||
return $this->subtractFee($rate);
|
||||
}
|
||||
|
||||
/** Get the base value of the money in the used currency. */
|
||||
public function value(): int
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/** Get the used currency. */
|
||||
public function currency(): Currency
|
||||
{
|
||||
return $this->currency;
|
||||
}
|
||||
|
||||
/** Get the decimal representation of the value. */
|
||||
public function decimal(): float
|
||||
{
|
||||
return $this->value / 10 ** $this->currency->mathDecimals();
|
||||
}
|
||||
|
||||
/** Format the value. */
|
||||
public function formatted(mixed ...$overrides): string
|
||||
{
|
||||
return PriceFormatter::format($this->decimal(), $this->currency, variadic_array($overrides));
|
||||
}
|
||||
|
||||
/** Format the raw (unrounded) value. */
|
||||
public function rawFormatted(mixed ...$overrides): string
|
||||
{
|
||||
return $this->formatted(array_merge(variadic_array($overrides), [
|
||||
'displayDecimals' => $this->currency->mathDecimals(),
|
||||
]));
|
||||
}
|
||||
|
||||
/** Get the string representation of the Money instance. */
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->formatted();
|
||||
}
|
||||
|
||||
/** Convert the instance to an array representation. */
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'value' => $this->value,
|
||||
'currency' => $this->currency->code(),
|
||||
];
|
||||
}
|
||||
|
||||
/** Check if the value equals the value of another Money instance, adjusted for currency. */
|
||||
public function equals(self $money): bool
|
||||
{
|
||||
return $this->valueInDefaultCurrency() === $money->valueInDefaultCurrency();
|
||||
}
|
||||
|
||||
/** Check if the value and currency match another Money instance. */
|
||||
public function is(self $money): bool
|
||||
{
|
||||
return $this->currency()->code() === $money->currency()->code()
|
||||
&& $this->equals($money);
|
||||
}
|
||||
|
||||
/** Get the data used for JSON serializing this object. */
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/** Convert the instance to JSON */
|
||||
public function toJson(): string
|
||||
{
|
||||
return json_encode($this, JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
/** Instantiate Money from JSON. */
|
||||
public static function fromJson(string|array $json): self
|
||||
{
|
||||
if (is_string($json)) {
|
||||
$json = json_decode($json, true, flags: JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
return new static($json['value'], $json['currency']);
|
||||
}
|
||||
|
||||
/** Value in the default currency. */
|
||||
public function valueInDefaultCurrency(): int
|
||||
{
|
||||
$mathDecimalDifference = $this->currency->mathDecimals() - currencies()->getDefault()->mathDecimals();
|
||||
|
||||
return $this
|
||||
->divideBy($this->currency->rate())
|
||||
->divideBy(10 ** $mathDecimalDifference)
|
||||
->value();
|
||||
}
|
||||
|
||||
/** Convert the money to a different currency. */
|
||||
public function convertTo(Currency|string $currency): self
|
||||
{
|
||||
// We're converting from the current currency to the default currency, and then to the intended currency
|
||||
$newCurrency = currency($currency);
|
||||
$mathDecimalDifference = $newCurrency->mathDecimals() - currencies()->getDefault()->mathDecimals();
|
||||
|
||||
return new static(
|
||||
(int) round($this->valueInDefaultCurrency() * $newCurrency->rate() * 10 ** $mathDecimalDifference, 0),
|
||||
$currency
|
||||
);
|
||||
}
|
||||
|
||||
/** Convert the Money to the current currency. */
|
||||
public function toCurrent(): self
|
||||
{
|
||||
return $this->convertTo(currencies()->getCurrent());
|
||||
}
|
||||
|
||||
/** Convert the Money to the current currency. */
|
||||
public function toDefault(): self
|
||||
{
|
||||
return $this->convertTo(currencies()->getDefault());
|
||||
}
|
||||
|
||||
/** Round the Money to a custom precision. */
|
||||
public function rounded(int $precision = null): self
|
||||
{
|
||||
$precision ??= $this->currency->rounding();
|
||||
|
||||
return $this->new(((int) round($this->value, -$precision)));
|
||||
}
|
||||
|
||||
/** Get the money rounding (typically this is the difference between the actual value and the formatted value.) */
|
||||
public function rounding(): int
|
||||
{
|
||||
return $this->rounded()->value() - $this->value();
|
||||
}
|
||||
|
||||
public function toLivewire()
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
public static function fromLivewire($value)
|
||||
{
|
||||
return static::fromJson($value);
|
||||
}
|
||||
}
|
||||
15
src/MoneyServiceProvider.php
Normal file
15
src/MoneyServiceProvider.php
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ArchTech\Money;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class MoneyServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function register(): void
|
||||
{
|
||||
$this->app->singleton(CurrencyManager::class);
|
||||
}
|
||||
}
|
||||
25
src/PriceFormatter.php
Normal file
25
src/PriceFormatter.php
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ArchTech\Money;
|
||||
|
||||
class PriceFormatter
|
||||
{
|
||||
/** Format a decimal per the currency's specifications. */
|
||||
public static function format(float $decimal, Currency $currency, array $overrides = []): string
|
||||
{
|
||||
$currency = Currency::fromArray(
|
||||
array_merge(currency($currency)->toArray(), $overrides)
|
||||
);
|
||||
|
||||
$decimal = number_format(
|
||||
$decimal,
|
||||
$currency->displayDecimals(),
|
||||
$currency->decimalSeparator(),
|
||||
$currency->thousandsSeparator(),
|
||||
);
|
||||
|
||||
return $currency->prefix() . $decimal . $currency->suffix();
|
||||
}
|
||||
}
|
||||
13
src/Wireable.php
Normal file
13
src/Wireable.php
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// Temporary until Livewire gets the new system for Wireable properties
|
||||
|
||||
namespace Livewire {
|
||||
interface Wireable
|
||||
{
|
||||
public function toLivewire();
|
||||
public static function fromLivewire($value);
|
||||
}
|
||||
}
|
||||
31
src/helpers.php
Normal file
31
src/helpers.php
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use ArchTech\Money\Currency;
|
||||
use ArchTech\Money\CurrencyManager;
|
||||
use ArchTech\Money\Money;
|
||||
|
||||
/** Create a Money instance. */
|
||||
function money(int $amount, Currency|string $currency = null): Money
|
||||
{
|
||||
return new Money($amount, $currency ?? currencies()->getDefault());
|
||||
}
|
||||
|
||||
/** Fetch a currency. If no argument is provided, the current currency will be returned. */
|
||||
function currency(Currency|string $currency = null): Currency
|
||||
{
|
||||
if ($currency) {
|
||||
return $currency instanceof Currency
|
||||
? $currency
|
||||
: currencies()->get($currency);
|
||||
}
|
||||
|
||||
return currencies()->getCurrent();
|
||||
}
|
||||
|
||||
/** Get the CurrencyManager instance. */
|
||||
function currencies(): CurrencyManager
|
||||
{
|
||||
return app(CurrencyManager::class);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue