mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-06-22 18:03:37 +02:00
Use BigDecimal for Currency exchange rate.
This commit is contained in:
parent
08267b88b0
commit
db4d7dc5fc
9 changed files with 135 additions and 20 deletions
|
@ -43,6 +43,7 @@ declare(strict_types=1);
|
||||||
namespace App\Command;
|
namespace App\Command;
|
||||||
|
|
||||||
use App\Entity\PriceInformations\Currency;
|
use App\Entity\PriceInformations\Currency;
|
||||||
|
use Brick\Math\BigDecimal;
|
||||||
use function count;
|
use function count;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Exchanger\Exception\Exception;
|
use Exchanger\Exception\Exception;
|
||||||
|
@ -122,10 +123,11 @@ class UpdateExchangeRatesCommand extends Command
|
||||||
|
|
||||||
//Iterate over each candidate and update exchange rate
|
//Iterate over each candidate and update exchange rate
|
||||||
foreach ($candidates as $currency) {
|
foreach ($candidates as $currency) {
|
||||||
|
/** @var Currency $currency */
|
||||||
try {
|
try {
|
||||||
$rate = $swap->latest($currency->getIsoCode().'/'.$this->base_current);
|
$rate = $swap->latest($currency->getIsoCode().'/'.$this->base_current);
|
||||||
$currency->setExchangeRate((string) $rate->getValue());
|
$currency->setExchangeRate(BigDecimal::of($rate->getValue()));
|
||||||
$io->note(sprintf('Set exchange rate of %s to %f', $currency->getIsoCode(), $currency->getExchangeRate()));
|
$io->note(sprintf('Set exchange rate of %s to %f', $currency->getIsoCode(), $currency->getExchangeRate()->toFloat()));
|
||||||
$this->em->persist($currency);
|
$this->em->persist($currency);
|
||||||
|
|
||||||
++$success_counter;
|
++$success_counter;
|
||||||
|
|
|
@ -153,7 +153,7 @@ class Supplier extends AbstractCompany
|
||||||
/**
|
/**
|
||||||
* Sets the shipping costs for an order with this supplier.
|
* Sets the shipping costs for an order with this supplier.
|
||||||
*
|
*
|
||||||
* @param string|null $shipping_costs a BigDecimal with the shipping costs
|
* @param BigDecimal|null $shipping_costs a BigDecimal with the shipping costs
|
||||||
*
|
*
|
||||||
* @return Supplier
|
* @return Supplier
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -45,6 +45,9 @@ namespace App\Entity\PriceInformations;
|
||||||
use App\Entity\Attachments\CurrencyAttachment;
|
use App\Entity\Attachments\CurrencyAttachment;
|
||||||
use App\Entity\Base\AbstractStructuralDBElement;
|
use App\Entity\Base\AbstractStructuralDBElement;
|
||||||
use App\Entity\Parameters\CurrencyParameter;
|
use App\Entity\Parameters\CurrencyParameter;
|
||||||
|
use App\Validator\Constraints\BigDecimal\BigDecimalPositive;
|
||||||
|
use Brick\Math\BigDecimal;
|
||||||
|
use Brick\Math\RoundingMode;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
@ -63,10 +66,10 @@ class Currency extends AbstractStructuralDBElement
|
||||||
public const PRICE_SCALE = 5;
|
public const PRICE_SCALE = 5;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string|null The exchange rate between this currency and the base currency
|
* @var BigDecimal|null The exchange rate between this currency and the base currency
|
||||||
* (how many base units the current currency is worth)
|
* (how many base units the current currency is worth)
|
||||||
* @ORM\Column(type="decimal", precision=11, scale=5, nullable=true)
|
* @ORM\Column(type="big_decimal", precision=11, scale=5, nullable=true)
|
||||||
* @Assert\Positive()
|
* @BigDecimalPositive()
|
||||||
*/
|
*/
|
||||||
protected $exchange_rate;
|
protected $exchange_rate;
|
||||||
|
|
||||||
|
@ -148,22 +151,22 @@ class Currency extends AbstractStructuralDBElement
|
||||||
/**
|
/**
|
||||||
* Returns the inverse exchange rate (how many of the current currency the base unit is worth).
|
* Returns the inverse exchange rate (how many of the current currency the base unit is worth).
|
||||||
*/
|
*/
|
||||||
public function getInverseExchangeRate(): ?string
|
public function getInverseExchangeRate(): ?BigDecimal
|
||||||
{
|
{
|
||||||
$tmp = $this->getExchangeRate();
|
$tmp = $this->getExchangeRate();
|
||||||
|
|
||||||
if (null === $tmp || '0' === $tmp) {
|
if (null === $tmp || $tmp->isZero()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return bcdiv('1', $tmp, static::PRICE_SCALE);
|
return BigDecimal::one()->dividedBy($tmp, $tmp->getScale(), RoundingMode::HALF_UP);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns The exchange rate between this currency and the base currency
|
* Returns The exchange rate between this currency and the base currency
|
||||||
* (how many base units the current currency is worth).
|
* (how many base units the current currency is worth).
|
||||||
*/
|
*/
|
||||||
public function getExchangeRate(): ?string
|
public function getExchangeRate(): ?BigDecimal
|
||||||
{
|
{
|
||||||
return $this->exchange_rate;
|
return $this->exchange_rate;
|
||||||
}
|
}
|
||||||
|
@ -171,12 +174,12 @@ class Currency extends AbstractStructuralDBElement
|
||||||
/**
|
/**
|
||||||
* Sets the exchange rate of the currency.
|
* Sets the exchange rate of the currency.
|
||||||
*
|
*
|
||||||
* @param string|null $exchange_rate The new exchange rate of the currency.
|
* @param BigDecimal|null $exchange_rate The new exchange rate of the currency.
|
||||||
* Set to null, if the exchange rate is unknown.
|
* Set to null, if the exchange rate is unknown.
|
||||||
*
|
*
|
||||||
* @return Currency
|
* @return Currency
|
||||||
*/
|
*/
|
||||||
public function setExchangeRate(?string $exchange_rate): self
|
public function setExchangeRate(?BigDecimal $exchange_rate): self
|
||||||
{
|
{
|
||||||
$this->exchange_rate = $exchange_rate;
|
$this->exchange_rate = $exchange_rate;
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ declare(strict_types=1);
|
||||||
namespace App\Form\AdminPages;
|
namespace App\Form\AdminPages;
|
||||||
|
|
||||||
use App\Entity\Base\AbstractNamedDBElement;
|
use App\Entity\Base\AbstractNamedDBElement;
|
||||||
|
use App\Form\Type\BigDecimalMoneyType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\CurrencyType;
|
use Symfony\Component\Form\Extension\Core\Type\CurrencyType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
|
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
@ -74,7 +75,7 @@ class CurrencyAdminForm extends BaseEntityAdminForm
|
||||||
'disabled' => ! $this->security->isGranted($is_new ? 'create' : 'edit', $entity),
|
'disabled' => ! $this->security->isGranted($is_new ? 'create' : 'edit', $entity),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$builder->add('exchange_rate', MoneyType::class, [
|
$builder->add('exchange_rate', BigDecimalMoneyType::class, [
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'label' => 'currency.edit.exchange_rate',
|
'label' => 'currency.edit.exchange_rate',
|
||||||
'currency' => $this->default_currency,
|
'currency' => $this->default_currency,
|
||||||
|
|
|
@ -213,11 +213,11 @@ class PricedetailHelper
|
||||||
$val_base = $value;
|
$val_base = $value;
|
||||||
if (null !== $originCurrency) {
|
if (null !== $originCurrency) {
|
||||||
//Without an exchange rate we can not calculate the exchange rate
|
//Without an exchange rate we can not calculate the exchange rate
|
||||||
if (0.0 === (float) $originCurrency->getExchangeRate()) {
|
if ($originCurrency->getExchangeRate() === null || $originCurrency->getExchangeRate()->isZero()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$val_base = bcmul($value, $originCurrency->getExchangeRate(), Pricedetail::PRICE_PRECISION);
|
$val_base = bcmul($value, (string) $originCurrency->getExchangeRate(), Pricedetail::PRICE_PRECISION);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Convert value in base currency to target currency
|
//Convert value in base currency to target currency
|
||||||
|
@ -228,7 +228,7 @@ class PricedetailHelper
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$val_target = bcmul($val_base, $targetCurrency->getInverseExchangeRate(), Pricedetail::PRICE_PRECISION);
|
$val_target = bcmul($val_base, (string) $targetCurrency->getInverseExchangeRate(), Pricedetail::PRICE_PRECISION);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $val_target;
|
return $val_target;
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019 - 2020 Jan Böhmer (https://github.com/jbtronics)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published
|
||||||
|
* by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Validator\Constraints\BigDecimal;
|
||||||
|
|
||||||
|
|
||||||
|
use Brick\Math\BigDecimal;
|
||||||
|
use Symfony\Component\Validator\Constraints\AbstractComparisonValidator;
|
||||||
|
use Symfony\Component\Validator\Constraints\GreaterThan;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates values are greater than the previous (>).
|
||||||
|
*
|
||||||
|
* @author Daniel Holmes <daniel@danielholmes.org>
|
||||||
|
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||||
|
*/
|
||||||
|
class BigDecimalGreaterThanValidator extends AbstractComparisonValidator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function compareValues($value1, $value2)
|
||||||
|
{
|
||||||
|
if ($value1 instanceof BigDecimal) {
|
||||||
|
$value1 = (string) $value1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value2 instanceof BigDecimal) {
|
||||||
|
$value2 = (string) $value2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null === $value2 || $value1 > $value2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getErrorCode()
|
||||||
|
{
|
||||||
|
return GreaterThan::TOO_LOW_ERROR;
|
||||||
|
}
|
||||||
|
}
|
49
src/Validator/Constraints/BigDecimal/BigDecimalPositive.php
Normal file
49
src/Validator/Constraints/BigDecimal/BigDecimalPositive.php
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019 - 2020 Jan Böhmer (https://github.com/jbtronics)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published
|
||||||
|
* by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Validator\Constraints\BigDecimal;
|
||||||
|
|
||||||
|
|
||||||
|
use Symfony\Component\Validator\Constraints\GreaterThan;
|
||||||
|
use Symfony\Component\Validator\Constraints\GreaterThanValidator;
|
||||||
|
use Symfony\Component\Validator\Constraints\NumberConstraintTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Annotation
|
||||||
|
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
|
||||||
|
*
|
||||||
|
* @author Jan Schädlich <jan.schaedlich@sensiolabs.de>
|
||||||
|
*/
|
||||||
|
class BigDecimalPositive extends GreaterThan
|
||||||
|
{
|
||||||
|
use NumberConstraintTrait;
|
||||||
|
|
||||||
|
public $message = 'This value should be positive.';
|
||||||
|
|
||||||
|
public function __construct($options = null)
|
||||||
|
{
|
||||||
|
parent::__construct($this->configureNumberConstraintOptions($options));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validatedBy(): string
|
||||||
|
{
|
||||||
|
return BigDecimalGreaterThanValidator::class;
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,7 @@
|
||||||
{{ form_row(form.exchange_rate) }}
|
{{ form_row(form.exchange_rate) }}
|
||||||
{% if entity.inverseExchangeRate %}
|
{% if entity.inverseExchangeRate %}
|
||||||
<span class="form-text text-muted offset-3 col-9">
|
<span class="form-text text-muted offset-3 col-9">
|
||||||
{{ '1'|format_currency(default_currency) }} = {{ entity.inverseExchangeRate | format_currency(entity.isoCode, {fraction_digit: 5}) }}
|
{{ '1'|format_currency(default_currency) }} = {{ entity.inverseExchangeRate.tofloat | format_currency(entity.isoCode, {fraction_digit: 5}) }}
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -43,6 +43,7 @@ declare(strict_types=1);
|
||||||
namespace App\Tests\Entity\PriceSystem;
|
namespace App\Tests\Entity\PriceSystem;
|
||||||
|
|
||||||
use App\Entity\PriceInformations\Currency;
|
use App\Entity\PriceInformations\Currency;
|
||||||
|
use Brick\Math\BigDecimal;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class CurrencyTest extends TestCase
|
class CurrencyTest extends TestCase
|
||||||
|
@ -54,10 +55,10 @@ class CurrencyTest extends TestCase
|
||||||
//By default the inverse exchange rate is not set:
|
//By default the inverse exchange rate is not set:
|
||||||
$this->assertNull($currency->getInverseExchangeRate());
|
$this->assertNull($currency->getInverseExchangeRate());
|
||||||
|
|
||||||
$currency->setExchangeRate('0');
|
$currency->setExchangeRate(BigDecimal::zero());
|
||||||
$this->assertNull($currency->getInverseExchangeRate());
|
$this->assertNull($currency->getInverseExchangeRate());
|
||||||
|
|
||||||
$currency->setExchangeRate('1.45643');
|
$currency->setExchangeRate(BigDecimal::of('1.45643'));
|
||||||
$this->assertSame('0.68661', $currency->getInverseExchangeRate());
|
$this->assertSame(BigDecimal::of('0.68661'), $currency->getInverseExchangeRate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue