diff --git a/composer.json b/composer.json index 7f001dc4..4ceda09c 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,7 @@ "ext-json": "*", "ext-mbstring": "*", "beberlei/doctrineextensions": "^1.2", + "brick/math": "^0.8.15", "doctrine/annotations": "^1.6", "doctrine/doctrine-bundle": "^2.0", "dompdf/dompdf": "^0.8.5", diff --git a/composer.lock b/composer.lock index e6cf5a4b..345cccdf 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "19cd1b3160aed1bd1f07a776339b462d", + "content-hash": "23b9810991f322cda669c851011f7bbf", "packages": [ { "name": "beberlei/assert", @@ -122,6 +122,52 @@ ], "time": "2019-12-05T09:49:04+00:00" }, + { + "name": "brick/math", + "version": "0.8.15", + "source": { + "type": "git", + "url": "https://github.com/brick/math.git", + "reference": "9b08d412b9da9455b210459ff71414de7e6241cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/math/zipball/9b08d412b9da9455b210459ff71414de7e6241cd", + "reference": "9b08d412b9da9455b210459ff71414de7e6241cd", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.1|^8.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^7.5.15|^8.5", + "vimeo/psalm": "^3.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Arbitrary-precision arithmetic library", + "keywords": [ + "Arbitrary-precision", + "BigInteger", + "BigRational", + "arithmetic", + "bigdecimal", + "bignum", + "brick", + "math" + ], + "time": "2020-04-15T15:59:35+00:00" + }, { "name": "doctrine/annotations", "version": "1.10.2", diff --git a/config/packages/doctrine.yaml b/config/packages/doctrine.yaml index 6dac8373..5d3bdc81 100644 --- a/config/packages/doctrine.yaml +++ b/config/packages/doctrine.yaml @@ -11,6 +11,8 @@ doctrine: class: App\Helpers\UTCDateTimeType date: class: App\Helpers\UTCDateTimeType + big_decimal: + class: App\Helpers\BigDecimalType schema_filter: ~^(?!internal)~ profiling_collect_backtrace: true diff --git a/src/Entity/Parts/Supplier.php b/src/Entity/Parts/Supplier.php index b2c464a5..7bf16083 100644 --- a/src/Entity/Parts/Supplier.php +++ b/src/Entity/Parts/Supplier.php @@ -54,7 +54,10 @@ use App\Entity\Attachments\SupplierAttachment; use App\Entity\Base\AbstractCompany; use App\Entity\Parameters\SupplierParameter; use App\Entity\PriceInformations\Currency; +use App\Validator\Constraints\BigDecimal\BigDecimalPositiveOrZero; use App\Validator\Constraints\Selectable; +use Brick\Math\BigDecimal; +use Brick\Math\BigNumber; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; @@ -94,9 +97,9 @@ class Supplier extends AbstractCompany protected $default_currency; /** - * @var string|null the shipping costs that have to be paid, when ordering via this supplier - * @ORM\Column(name="shipping_costs", nullable=true, type="decimal", precision=11, scale=5) - * @Assert\PositiveOrZero() + * @var BigDecimal|null the shipping costs that have to be paid, when ordering via this supplier + * @ORM\Column(name="shipping_costs", nullable=true, type="big_decimal", precision=11, scale=5) + * @BigDecimalPositiveOrZero() */ protected $shipping_costs; @@ -140,9 +143,9 @@ class Supplier extends AbstractCompany /** * Gets the shipping costs for an order with this supplier, given in base currency. * - * @return string|null A bcmath string with the shipping costs + * @return BigDecimal|null A BigDecimal with the shipping costs */ - public function getShippingCosts(): ?string + public function getShippingCosts(): ?BigDecimal { return $this->shipping_costs; } @@ -150,17 +153,13 @@ class Supplier extends AbstractCompany /** * Sets the shipping costs for an order with this supplier. * - * @param string|null $shipping_costs a bcmath string with the shipping costs + * @param string|null $shipping_costs a BigDecimal with the shipping costs * * @return Supplier */ - public function setShippingCosts(?string $shipping_costs): self + public function setShippingCosts(?BigDecimal $shipping_costs): self { - /* Just a little hack to ensure that price has 5 digits after decimal point, - so that DB does not detect changes, when something like 0.4 is passed - Third parameter must have the scale value of decimal column. */ - $this->shipping_costs = bcmul($shipping_costs, '1.0', 5); - + $this->shipping_costs = $shipping_costs; return $this; } } diff --git a/src/Form/AdminPages/SupplierForm.php b/src/Form/AdminPages/SupplierForm.php index da07c6c0..17a39ce3 100644 --- a/src/Form/AdminPages/SupplierForm.php +++ b/src/Form/AdminPages/SupplierForm.php @@ -44,6 +44,7 @@ namespace App\Form\AdminPages; use App\Entity\Base\AbstractNamedDBElement; use App\Entity\PriceInformations\Currency; +use App\Form\Type\BigDecimalMoneyType; use App\Form\Type\StructuralEntityType; use Symfony\Component\Form\Extension\Core\Type\MoneyType; use Symfony\Component\Form\FormBuilderInterface; @@ -73,7 +74,7 @@ class SupplierForm extends CompanyForm 'disabled' => ! $this->security->isGranted($is_new ? 'create' : 'move', $entity), ]); - $builder->add('shipping_costs', MoneyType::class, [ + $builder->add('shipping_costs', BigDecimalMoneyType::class, [ 'required' => false, 'currency' => $this->default_currency, 'scale' => 3, diff --git a/src/Form/Type/BigDecimalMoneyType.php b/src/Form/Type/BigDecimalMoneyType.php new file mode 100644 index 00000000..163e0786 --- /dev/null +++ b/src/Form/Type/BigDecimalMoneyType.php @@ -0,0 +1,65 @@ +. + */ + +namespace App\Form\Type; + + +use Brick\Math\BigDecimal; +use Brick\Math\BigNumber; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Extension\Core\Type\MoneyType; +use Symfony\Component\Form\FormBuilderInterface; + +class BigDecimalMoneyType extends AbstractType implements DataTransformerInterface +{ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->addModelTransformer($this); + } + + public function getParent() + { + return MoneyType::class; + } + + public function transform($value) + { + if ($value === null) { + return null; + } + + if ($value instanceof BigDecimal) { + return (string) $value; + } + + return $value; + } + + public function reverseTransform($value) + { + if ($value === null) { + return null; + } + + return BigDecimal::of($value); + } +} \ No newline at end of file diff --git a/src/Helpers/BigDecimalType.php b/src/Helpers/BigDecimalType.php new file mode 100644 index 00000000..49fb3b07 --- /dev/null +++ b/src/Helpers/BigDecimalType.php @@ -0,0 +1,74 @@ +. + */ + +namespace App\Helpers; + + +use Brick\Math\BigDecimal; +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Types\Type; + +class BigDecimalType extends Type +{ + public const BIG_DECIMAL = 'big_decimal'; + + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + return $platform->getDecimalTypeDeclarationSQL($fieldDeclaration); + } + + /** + * @param string $value + * @param AbstractPlatform $platform + * @return BigDecimal|\Brick\Math\BigNumber|mixed + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return null; + } + + return BigDecimal::of($value); + } + + /** + * @param BigDecimal $value + * @param AbstractPlatform $platform + * @return mixed + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return null; + } + + return (string) $value; + } + + public function getName() + { + return self::BIG_DECIMAL; + } + + public function requiresSQLCommentHint(AbstractPlatform $platform) + { + return true; + } +} \ No newline at end of file diff --git a/src/Validator/Constraints/BigDecimal/BigDecimalGreaterThenOrEqualValidator.php b/src/Validator/Constraints/BigDecimal/BigDecimalGreaterThenOrEqualValidator.php new file mode 100644 index 00000000..dd1887b0 --- /dev/null +++ b/src/Validator/Constraints/BigDecimal/BigDecimalGreaterThenOrEqualValidator.php @@ -0,0 +1,59 @@ +. + */ + +namespace App\Validator\Constraints\BigDecimal; + + +use Brick\Math\BigDecimal; +use Symfony\Component\Validator\Constraints\AbstractComparisonValidator; +use Symfony\Component\Validator\Constraints\GreaterThanOrEqual; + +/** + * Validates values are greater than or equal to the previous (>=). + * + * @author Daniel Holmes + * @author Bernhard Schussek + */ +class BigDecimalGreaterThenOrEqualValidator 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 GreaterThanOrEqual::TOO_LOW_ERROR; + } +} diff --git a/src/Validator/Constraints/BigDecimal/BigDecimalPositiveOrZero.php b/src/Validator/Constraints/BigDecimal/BigDecimalPositiveOrZero.php new file mode 100644 index 00000000..748ed1c2 --- /dev/null +++ b/src/Validator/Constraints/BigDecimal/BigDecimalPositiveOrZero.php @@ -0,0 +1,48 @@ +. + */ + +namespace App\Validator\Constraints\BigDecimal; + + +use Symfony\Component\Validator\Constraints\GreaterThanOrEqual; +use Symfony\Component\Validator\Constraints\NumberConstraintTrait; + +/** + * @Annotation + * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) + * + * @author Jan Schädlich + */ +class BigDecimalPositiveOrZero extends GreaterThanOrEqual +{ + use NumberConstraintTrait; + + public $message = 'This value should be either positive or zero.'; + + public function __construct($options = null) + { + parent::__construct($this->configureNumberConstraintOptions($options)); + } + + public function validatedBy(): string + { + return BigDecimalGreaterThenOrEqualValidator::class; + } +} \ No newline at end of file diff --git a/symfony.lock b/symfony.lock index fa4fdd15..e6fb01b3 100644 --- a/symfony.lock +++ b/symfony.lock @@ -11,6 +11,9 @@ "beberlei/doctrineextensions": { "version": "v1.2.6" }, + "brick/math": { + "version": "0.8.15" + }, "composer/semver": { "version": "1.5.0" },