Skip to content

Instantly share code, notes, and snippets.

@unglud
Created October 25, 2017 09:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save unglud/3215dd0e5f77ecad135048fd56533e66 to your computer and use it in GitHub Desktop.
Save unglud/3215dd0e5f77ecad135048fd56533e66 to your computer and use it in GitHub Desktop.
<?php
namespace Core\App\Service\Money;
use Money\Currencies\ISOCurrencies;
use Money\Formatter\DecimalMoneyFormatter;
use Money\Parser\DecimalMoneyParser;
class Money
{
private $entity;
private $money;
private $override = [
'getAmount',
'setAmount',
'getAmountNet',
'setAmountNet',
'getCommissionFee',
'getCommissionFeeNet',
'getTransactionFee',
'getTransactionFeeNet',
'getManual',
'getManualNet',
'getValue',
];
/**
* Money constructor.
* @param int $amount
* @param \Money\Money|null $money
*/
public function __construct($amount = 0, \Money\Money $money = null)
{
if ($money !== null) {
$this->money = $money;
} else {
$this->money = $this->parseFloat($amount);
}
}
/**
* Convert any entity object to be able return money object instead floats
* @param EntityInterface $entity
* @return Money
*/
public static function createFromEntity(EntityInterface $entity)
{
$money = new self();
$money->setEntity($entity);
return $money;
}
/**
* @param $name
* @param $arguments
* @return bool|mixed
* @throws \Exception
*/
public function __call($name, $arguments)
{
if ($this->entity === null) {
throw new \Exception(
"You must create Money object from entity (Money::createFromEntity) to use method $name");
}
if (current($arguments) instanceof Money) {
/** @var Money[] $arguments */
$arguments[0] = $arguments[0]->toString();
}
if ($name === 'getAmountNet' && $this->entity instanceof Booking) {
return $this->getNet();
}
if (!method_exists($this->entity, $name)) {
$class = get_class($this->entity);
throw new \Exception(
"Method $name doesn't exist in class $class");
}
$result = call_user_func_array([$this->entity, $name], $arguments);
// if it setter
if (is_object($result) && get_class($result) === get_class($this->entity)) {
return $this;
}
if ($this->isOverride($name)) {
$result = new self($result);
}
return $result;
}
/**
* @return string
*/
public function __toString()
{
return $this->toString(true);
}
/**
* @param $addend
* @return Money
*/
public function add($addend)
{
/** @var Money $addend */
return new self(0, $this->money->add($addend->getMoneyObject()));
}
/**
* @param $subtrahend
* @return Money
*/
public function subtract($subtrahend)
{
/** @var Money $subtrahend */
return new self(0, $this->money->subtract($subtrahend->getMoneyObject()));
}
/**
* @return bool
*/
public function isNegative()
{
return $this->money->isNegative();
}
/**
* @return bool
*/
public function isZero()
{
return $this->money->isZero();
}
/**
* @return mixed
*/
public function getEntity()
{
return $this->entity;
}
/**
* @param bool $print
* @return string
*/
public function toString($print = false)
{
$currencies = new ISOCurrencies();
$moneyFormatter = new DecimalMoneyFormatter($currencies);
$formatted = $moneyFormatter->format($this->money);
if ($print) {
return number_format((float)$formatted, 2, ',', '.');
}
return $formatted;
}
/**
* @return Money
*/
public function absolute()
{
return new self(0, $this->money->absolute());
}
/**
* @return Money
*/
public function negative()
{
return new self(0, $this->money->negative());
}
/**
* @return int
*/
private function toInt()
{
return (int)$this->toString();
}
/**
* @param EntityInterface $entity
*/
private function setEntity(EntityInterface $entity)
{
$this->entity = $entity;
}
/**
* @param $float
* @return \Money\Money
*/
private function parseFloat($float)
{
$currencies = new ISOCurrencies();
$moneyParser = new DecimalMoneyParser($currencies);
$formattedFloat = sprintf("%f", $float);
return $moneyParser->parse($formattedFloat, 'EUR');
}
/**
* @return \Money\Money
*/
public function getMoneyObject()
{
return $this->money;
}
/**
* @param $name
* @return bool
*/
private function isOverride($name)
{
return in_array($name, $this->override);
}
/**
* @param int $tax
* @return Money
*/
public function getNet($tax = 0)
{
$money = $this->calculateTax($tax);
return $money;
}
/**
* @return Money
*/
public function getTax()
{
$subtract = $this->getAmount()->subtract($this->getNet());
return $subtract;
}
/**
* Allocate percentage
*
* @param int|float|string $percentage
* @return Money
*/
public function getPercent($percentage)
{
$base = new self(100);
$right = new self($percentage);
$left = $base->subtract($right);
// allocation put reminder of dividing to left side, if we split 3 to 50%, we will get 2 and 1
// for percentages we do not need this, 'cuz reminder happen if percent is >= 50
list($reminder, $cut) = $this->money->allocate([$left->toFloat(), $right->toFloat()]);
return new self(0, $cut);
}
/**
* @param array $ratios
* @return \Money\Money[]
*/
public function allocate(array $ratios)
{
return $this->money->allocate($ratios);
}
/**
* @param Money $divisor
* @return static
*/
public function divide($divisor)
{
return new self(0, $this->money->divide($divisor->toInt()));
}
/**
* @param Money $multiplier
* @return static
*/
public function multiply($multiplier)
{
return new self(0, $this->money->multiply($multiplier->toInt()));
}
/**
* @param int $tax
* @return Money
*/
private function calculateTax($tax = 0)
{
$taxrate = $tax;
if (!$taxrate && method_exists($this->entity, 'getTaxrate')) {
$taxrate = $this->entity->getTaxrate();
}
$tax = new self($taxrate + 100); // 19% for example
$full = new self(100);
if ($this->entity === null) {
return new self(0, $this->money->multiply($full->toInt())->divide($tax->toInt()));
}
return $this->getAmount()->multiply($full)->divide($tax);
}
/**
* @return float
*/
private function toFloat()
{
return (float)$this->toString();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment