mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-06-21 01:25:55 +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;
|
||||
|
||||
use App\Entity\PriceInformations\Currency;
|
||||
use Brick\Math\BigDecimal;
|
||||
use function count;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Exchanger\Exception\Exception;
|
||||
|
@ -122,10 +123,11 @@ class UpdateExchangeRatesCommand extends Command
|
|||
|
||||
//Iterate over each candidate and update exchange rate
|
||||
foreach ($candidates as $currency) {
|
||||
/** @var Currency $currency */
|
||||
try {
|
||||
$rate = $swap->latest($currency->getIsoCode().'/'.$this->base_current);
|
||||
$currency->setExchangeRate((string) $rate->getValue());
|
||||
$io->note(sprintf('Set exchange rate of %s to %f', $currency->getIsoCode(), $currency->getExchangeRate()));
|
||||
$currency->setExchangeRate(BigDecimal::of($rate->getValue()));
|
||||
$io->note(sprintf('Set exchange rate of %s to %f', $currency->getIsoCode(), $currency->getExchangeRate()->toFloat()));
|
||||
$this->em->persist($currency);
|
||||
|
||||
++$success_counter;
|
||||
|
|
|
@ -153,7 +153,7 @@ class Supplier extends AbstractCompany
|
|||
/**
|
||||
* 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
|
||||
*/
|
||||
|
|
|
@ -45,6 +45,9 @@ namespace App\Entity\PriceInformations;
|
|||
use App\Entity\Attachments\CurrencyAttachment;
|
||||
use App\Entity\Base\AbstractStructuralDBElement;
|
||||
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\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
@ -63,10 +66,10 @@ class Currency extends AbstractStructuralDBElement
|
|||
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)
|
||||
* @ORM\Column(type="decimal", precision=11, scale=5, nullable=true)
|
||||
* @Assert\Positive()
|
||||
* @ORM\Column(type="big_decimal", precision=11, scale=5, nullable=true)
|
||||
* @BigDecimalPositive()
|
||||
*/
|
||||
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).
|
||||
*/
|
||||
public function getInverseExchangeRate(): ?string
|
||||
public function getInverseExchangeRate(): ?BigDecimal
|
||||
{
|
||||
$tmp = $this->getExchangeRate();
|
||||
|
||||
if (null === $tmp || '0' === $tmp) {
|
||||
if (null === $tmp || $tmp->isZero()) {
|
||||
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
|
||||
* (how many base units the current currency is worth).
|
||||
*/
|
||||
public function getExchangeRate(): ?string
|
||||
public function getExchangeRate(): ?BigDecimal
|
||||
{
|
||||
return $this->exchange_rate;
|
||||
}
|
||||
|
@ -171,12 +174,12 @@ class Currency extends AbstractStructuralDBElement
|
|||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return Currency
|
||||
*/
|
||||
public function setExchangeRate(?string $exchange_rate): self
|
||||
public function setExchangeRate(?BigDecimal $exchange_rate): self
|
||||
{
|
||||
$this->exchange_rate = $exchange_rate;
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ declare(strict_types=1);
|
|||
namespace App\Form\AdminPages;
|
||||
|
||||
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\MoneyType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
@ -74,7 +75,7 @@ class CurrencyAdminForm extends BaseEntityAdminForm
|
|||
'disabled' => ! $this->security->isGranted($is_new ? 'create' : 'edit', $entity),
|
||||
]);
|
||||
|
||||
$builder->add('exchange_rate', MoneyType::class, [
|
||||
$builder->add('exchange_rate', BigDecimalMoneyType::class, [
|
||||
'required' => false,
|
||||
'label' => 'currency.edit.exchange_rate',
|
||||
'currency' => $this->default_currency,
|
||||
|
|
|
@ -213,11 +213,11 @@ class PricedetailHelper
|
|||
$val_base = $value;
|
||||
if (null !== $originCurrency) {
|
||||
//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;
|
||||
}
|
||||
|
||||
$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
|
||||
|
@ -228,7 +228,7 @@ class PricedetailHelper
|
|||
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;
|
||||
|
|
|
@ -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) }}
|
||||
{% if entity.inverseExchangeRate %}
|
||||
<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>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -43,6 +43,7 @@ declare(strict_types=1);
|
|||
namespace App\Tests\Entity\PriceSystem;
|
||||
|
||||
use App\Entity\PriceInformations\Currency;
|
||||
use Brick\Math\BigDecimal;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class CurrencyTest extends TestCase
|
||||
|
@ -54,10 +55,10 @@ class CurrencyTest extends TestCase
|
|||
//By default the inverse exchange rate is not set:
|
||||
$this->assertNull($currency->getInverseExchangeRate());
|
||||
|
||||
$currency->setExchangeRate('0');
|
||||
$currency->setExchangeRate(BigDecimal::zero());
|
||||
$this->assertNull($currency->getInverseExchangeRate());
|
||||
|
||||
$currency->setExchangeRate('1.45643');
|
||||
$this->assertSame('0.68661', $currency->getInverseExchangeRate());
|
||||
$currency->setExchangeRate(BigDecimal::of('1.45643'));
|
||||
$this->assertSame(BigDecimal::of('0.68661'), $currency->getInverseExchangeRate());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue