diff --git a/README.md b/README.md index 758a31e..cb21cbc 100644 --- a/README.md +++ b/README.md @@ -599,6 +599,9 @@ $totalCustomTypeModifiers = $price->modifiers(false, 'custom'); $totalCustomTypeModifiersPerUnit = $price->modifiers(true, 'custom'); ``` +> [!WARNING] +> These values return instances of `RationalMoney` instead of regular `Money`, this is to allow your modifiers to return `RationalMoney` values as well. `RationalMoney` objects do not require rounding when doing operations on them. If you want to transform them to regular `Money` objects, you must specify the context and rounding options. [See the `brick/money` documentation](https://github.com/brick/money?tab=readme-ov-file#advanced-calculations) for more info. + ## Output By default, all handled monetary values are wrapped into a `Brick\Money\Money` object. This should be the only way to manipulate these values in order to [avoid decimal approximation errors](https://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency). diff --git a/composer.json b/composer.json index 1505ac4..e009203 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "prefer-stable": true, "require": { "php": "^8.0", - "brick/money": "0.8.*" + "brick/money": "^0.9.0" }, "require-dev": { "pestphp/pest": "^1.0" diff --git a/composer.lock b/composer.lock index 9229263..5fc8ea8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,29 +4,29 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6e92af74b1c8d787ec32efa6cf2ee08a", + "content-hash": "f1d87500b1035169f65f9bfb952788d2", "packages": [ { "name": "brick/math", - "version": "0.11.0", + "version": "0.12.1", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478" + "reference": "f510c0a40911935b77b86859eb5223d58d660df1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/0ad82ce168c82ba30d1c01ec86116ab52f589478", - "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478", + "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", + "reference": "f510c0a40911935b77b86859eb5223d58d660df1", "shasum": "" }, "require": { - "php": "^8.0" + "php": "^8.1" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^9.0", - "vimeo/psalm": "5.0.0" + "phpunit/phpunit": "^10.1", + "vimeo/psalm": "5.16.0" }, "type": "library", "autoload": { @@ -46,12 +46,17 @@ "arithmetic", "bigdecimal", "bignum", + "bignumber", "brick", - "math" + "decimal", + "integer", + "math", + "mathematics", + "rational" ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.11.0" + "source": "https://github.com/brick/math/tree/0.12.1" }, "funding": [ { @@ -59,34 +64,34 @@ "type": "github" } ], - "time": "2023-01-15T23:15:59+00:00" + "time": "2023-11-29T23:19:16+00:00" }, { "name": "brick/money", - "version": "0.8.1", + "version": "0.9.0", "source": { "type": "git", "url": "https://github.com/brick/money.git", - "reference": "25f484a347756b7f3fbe7ad63ed9ad2d87b20004" + "reference": "60f8b6ceab2e1c9527e625198a76498fa477804a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/money/zipball/25f484a347756b7f3fbe7ad63ed9ad2d87b20004", - "reference": "25f484a347756b7f3fbe7ad63ed9ad2d87b20004", + "url": "https://api.github.com/repos/brick/money/zipball/60f8b6ceab2e1c9527e625198a76498fa477804a", + "reference": "60f8b6ceab2e1c9527e625198a76498fa477804a", "shasum": "" }, "require": { - "brick/math": "~0.10.1 || ~0.11.0", + "brick/math": "~0.12.0", "ext-json": "*", - "php": "^8.0" + "php": "^8.1" }, "require-dev": { "brick/varexporter": "~0.3.0", "ext-dom": "*", "ext-pdo": "*", "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^9.4.3", - "vimeo/psalm": "5.14.1" + "phpunit/phpunit": "^10.1", + "vimeo/psalm": "5.16.0" }, "suggest": { "ext-intl": "Required to format Money objects" @@ -109,7 +114,7 @@ ], "support": { "issues": "https://github.com/brick/money/issues", - "source": "https://github.com/brick/money/tree/0.8.1" + "source": "https://github.com/brick/money/tree/0.9.0" }, "funding": [ { @@ -117,7 +122,7 @@ "type": "github" } ], - "time": "2023-09-23T21:17:11+00:00" + "time": "2023-11-26T16:51:39+00:00" } ], "packages-dev": [ @@ -324,16 +329,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.0.2", + "version": "v5.1.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", "shasum": "" }, "require": { @@ -344,7 +349,7 @@ }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -376,9 +381,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" }, - "time": "2024-03-05T20:51:40+00:00" + "time": "2024-07-01T20:03:41+00:00" }, { "name": "nunomaduro/collision", @@ -753,35 +758,35 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.31", + "version": "9.2.32", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.18 || ^5.0", + "nikic/php-parser": "^4.19.1 || ^5.1.0", "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-text-template": "^2.0.4", + "sebastian/code-unit-reverse-lookup": "^2.0.3", + "sebastian/complexity": "^2.0.3", + "sebastian/environment": "^5.1.5", + "sebastian/lines-of-code": "^1.0.4", + "sebastian/version": "^3.0.2", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.6" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -790,7 +795,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "9.2.x-dev" } }, "autoload": { @@ -819,7 +824,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" }, "funding": [ { @@ -827,7 +832,7 @@ "type": "github" } ], - "time": "2024-03-02T06:37:42+00:00" + "time": "2024-08-22T04:23:01+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1072,45 +1077,45 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.19", + "version": "9.6.20", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8" + "reference": "49d7820565836236411f5dc002d16dd689cde42f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a1a54a473501ef4cdeaae4e06891674114d79db8", - "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/49d7820565836236411f5dc002d16dd689cde42f", + "reference": "49d7820565836236411f5dc002d16dd689cde42f", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1 || ^2", + "doctrine/instantiator": "^1.5.0 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", + "myclabs/deep-copy": "^1.12.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.28", - "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-code-coverage": "^9.2.31", + "phpunit/php-file-iterator": "^3.0.6", "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", + "phpunit/php-text-template": "^2.0.4", + "phpunit/php-timer": "^5.0.3", + "sebastian/cli-parser": "^1.0.2", + "sebastian/code-unit": "^1.0.8", "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.6", + "sebastian/global-state": "^5.0.7", + "sebastian/object-enumerator": "^4.0.4", + "sebastian/resource-operations": "^3.0.4", + "sebastian/type": "^3.2.1", "sebastian/version": "^3.0.2" }, "suggest": { @@ -1155,7 +1160,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.19" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.20" }, "funding": [ { @@ -1171,7 +1176,7 @@ "type": "tidelift" } ], - "time": "2024-04-05T04:35:58+00:00" + "time": "2024-07-10T11:45:39+00:00" }, { "name": "psr/container", @@ -1228,16 +1233,16 @@ }, { "name": "psr/log", - "version": "3.0.0", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { @@ -1272,9 +1277,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2021-07-14T16:46:02+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { "name": "sebastian/cli-parser", @@ -2241,16 +2246,16 @@ }, { "name": "symfony/console", - "version": "v6.4.8", + "version": "v6.4.11", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "be5854cee0e8c7b110f00d695d11debdfa1a2a91" + "reference": "42686880adaacdad1835ee8fc2a9ec5b7bd63998" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/be5854cee0e8c7b110f00d695d11debdfa1a2a91", - "reference": "be5854cee0e8c7b110f00d695d11debdfa1a2a91", + "url": "https://api.github.com/repos/symfony/console/zipball/42686880adaacdad1835ee8fc2a9ec5b7bd63998", + "reference": "42686880adaacdad1835ee8fc2a9ec5b7bd63998", "shasum": "" }, "require": { @@ -2315,7 +2320,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.8" + "source": "https://github.com/symfony/console/tree/v6.4.11" }, "funding": [ { @@ -2331,7 +2336,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2024-08-15T22:48:29+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2402,20 +2407,20 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -2461,7 +2466,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { @@ -2477,24 +2482,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -2539,7 +2544,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" }, "funding": [ { @@ -2555,24 +2560,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -2620,7 +2625,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" }, "funding": [ { @@ -2636,24 +2641,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -2700,7 +2705,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -2716,7 +2721,7 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/service-contracts", @@ -2803,16 +2808,16 @@ }, { "name": "symfony/string", - "version": "v7.1.1", + "version": "v7.1.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "60bc311c74e0af215101235aa6f471bcbc032df2" + "reference": "6cd670a6d968eaeb1c77c2e76091c45c56bc367b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/60bc311c74e0af215101235aa6f471bcbc032df2", - "reference": "60bc311c74e0af215101235aa6f471bcbc032df2", + "url": "https://api.github.com/repos/symfony/string/zipball/6cd670a6d968eaeb1c77c2e76091c45c56bc367b", + "reference": "6cd670a6d968eaeb1c77c2e76091c45c56bc367b", "shasum": "" }, "require": { @@ -2870,7 +2875,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.1" + "source": "https://github.com/symfony/string/tree/v7.1.4" }, "funding": [ { @@ -2886,7 +2891,7 @@ "type": "tidelift" } ], - "time": "2024-06-04T06:40:14+00:00" + "time": "2024-08-12T09:59:40+00:00" }, { "name": "theseer/tokenizer", diff --git a/src/Calculator.php b/src/Calculator.php index 77632cc..b58fda7 100644 --- a/src/Calculator.php +++ b/src/Calculator.php @@ -3,7 +3,7 @@ namespace Whitecube\Price; use Brick\Money\Money; -use Brick\Money\AbstractMoney; +use Brick\Money\RationalMoney; use Whitecube\Price\Price; class Calculator @@ -38,7 +38,7 @@ public function exclusiveBeforeVat(bool $perUnit): array /** * Return the result for the "VAT" Money build */ - public function vat(bool $perUnit): array|AbstractMoney + public function vat(bool $perUnit): array|RationalMoney { return $this->getCached('vat', $perUnit) ?? $this->setCached('vat', $perUnit, $this->getVatResult($perUnit)); @@ -65,7 +65,7 @@ public function inclusive(bool $perUnit): array /** * Retrieve a cached value for key and unit mode */ - protected function getCached(string $key, bool $perUnit): null|array|AbstractMoney + protected function getCached(string $key, bool $perUnit): null|array|RationalMoney { return $this->cache[$key][$perUnit ? 'unit' : 'all'] ?? null; } @@ -73,7 +73,7 @@ protected function getCached(string $key, bool $perUnit): null|array|AbstractMon /** * Set a cached value for key and unit mode */ - protected function setCached(string $key, bool $perUnit, array|AbstractMoney $result): array|AbstractMoney + protected function setCached(string $key, bool $perUnit, array|RationalMoney $result): array|RationalMoney { $this->cache[$key][$perUnit ? 'unit' : 'all'] = $result; @@ -123,12 +123,12 @@ protected function getExclusiveAfterVatResult(bool $perUnit): array /** * Compute the VAT */ - protected function getVatResult(bool $perUnit): AbstractMoney + protected function getVatResult(bool $perUnit): RationalMoney { $vat = $this->price->vat(true); if(! $vat) { - return Money::zero($this->price->currency(), $this->price->context()); + return Money::zero($this->price->currency(), $this->price->context())->toRational(); } $exclusive = $this->exclusiveBeforeVat($perUnit)['amount']; @@ -143,11 +143,11 @@ protected function getInclusiveResult(bool $perUnit): array { $result = $this->exclusiveBeforeVat($perUnit); - $result['amount'] = $result['amount']->plus($this->vat($perUnit), Price::getRounding('vat')); + $result['amount'] = $result['amount']->plus($this->vat($perUnit)); $supplement = $this->exclusiveAfterVat($perUnit); - $result['amount'] = $result['amount']->plus($supplement['amount'], Price::getRounding('exclusive')); + $result['amount'] = $result['amount']->plus($supplement['amount']); $result['modifications'] = array_merge($result['modifications'], $supplement['modifications']); return $result; @@ -156,7 +156,7 @@ protected function getInclusiveResult(bool $perUnit): array /** * Compute the price for one unit before VAT is applied */ - protected function applyModifier(PriceAmendable $modifier, array $result, bool $perUnit, bool $postVat = false, AbstractMoney $exclusive = null, Vat $vat = null): array + protected function applyModifier(PriceAmendable $modifier, array $result, bool $perUnit, bool $postVat = false, RationalMoney $exclusive = null, Vat $vat = null): array { $updated = $modifier->apply($result['amount'], $this->price->units(), $perUnit, $exclusive, $vat); diff --git a/src/Concerns/HasModifiers.php b/src/Concerns/HasModifiers.php index 2aa1391..c54e4c3 100644 --- a/src/Concerns/HasModifiers.php +++ b/src/Concerns/HasModifiers.php @@ -6,7 +6,7 @@ use Whitecube\Price\Modifier; use Whitecube\Price\PriceAmendable; use Brick\Money\AbstractMoney; -use Brick\Money\Money; +use Brick\Money\RationalMoney; trait HasModifiers { @@ -68,7 +68,7 @@ protected function makeModifier(string $type, string|callable|AbstractMoney|Pric $modifier = $modifier->inclusive(); } - return $instance->add($modifier, Price::getRounding('exclusive')); + return $instance->add($modifier); } /** @@ -101,7 +101,7 @@ public function modifications(bool $perUnit = false, ?string $type = null): arra /** * Return the modification total for all discounts */ - public function discounts(bool $perUnit = false): Money + public function discounts(bool $perUnit = false): RationalMoney { return $this->modifiers($perUnit, Modifier::TYPE_DISCOUNT); } @@ -109,7 +109,7 @@ public function discounts(bool $perUnit = false): Money /** * Return the modification total for all taxes */ - public function taxes(bool $perUnit = false): Money + public function taxes(bool $perUnit = false): RationalMoney { return $this->modifiers($perUnit, Modifier::TYPE_TAX); } @@ -117,9 +117,9 @@ public function taxes(bool $perUnit = false): Money /** * Return the modification total for a given type */ - public function modifiers(bool $perUnit = false, ?string $type = null): Money + public function modifiers(bool $perUnit = false, ?string $type = null): RationalMoney { - $amount = Money::zero($this->currency()); + $amount = RationalMoney::of(0, $this->currency()); foreach ($this->modifications($perUnit, $type) as $modification) { $amount = $amount->plus($modification['amount']); diff --git a/src/Concerns/OperatesOnBase.php b/src/Concerns/OperatesOnBase.php index cb6888a..0f35b47 100644 --- a/src/Concerns/OperatesOnBase.php +++ b/src/Concerns/OperatesOnBase.php @@ -4,6 +4,7 @@ use Whitecube\Price\Price; use Brick\Money\AbstractMoney; +use Brick\Money\RationalMoney; use Brick\Money\Exception\MoneyMismatchException; use Brick\Money\Money; use Brick\Math\BigNumber; @@ -21,7 +22,7 @@ public function __call(string $method, array $arguments): mixed $result = call_user_func_array([$this->base, $method], $arguments); - if(! is_a($result, AbstractMoney::class)) { + if(! is_a($result, RationalMoney::class)) { return $result; } @@ -33,64 +34,48 @@ public function __call(string $method, array $arguments): mixed } /** - * Check if given value equals the price's base value + * Check if the provided amount equals this price's total inclusive amount */ - public function equals(BigNumber|int|float|string|AbstractMoney|Price $value): bool + public function equals(BigNumber|int|float|string|AbstractMoney|Price $that): bool { - return $this->compareTo($value) === 0; + return $this->compareTo($that) === 0; } /** - * Compare a given value to the total inclusive value of this instance + * Compare the provided amount to this price's total inclusive amount */ - public function compareTo(BigNumber|int|float|string|AbstractMoney|Price $value): int + public function compareTo(BigNumber|int|float|string|AbstractMoney|Price $that): int { return $this->compareMonies( $this->inclusive(), - $this->valueToMoney($value) + $this->valueToRationalMoney($that, 'inclusive') ); } /** - * Compare a given value to the unitless base value of this instance + * Compare a provided amount to this price's unitless base amount */ - public function compareBaseTo(BigNumber|int|float|string|AbstractMoney|Price $value): int + public function compareBaseTo(BigNumber|int|float|string|AbstractMoney|Price $that): int { return $this->compareMonies( $this->base(), - $this->valueToMoney($value, 'base') + $this->valueToRationalMoney($that, 'base') ); } /** - * Compare the given "current" value to another value + * Compare the provided "current" amount to another amount * @throws \Brick\Money\Exception\MoneyMismatchException */ - protected function compareMonies(AbstractMoney $price, AbstractMoney $that): int + protected function compareMonies(RationalMoney $current, RationalMoney $that): int { - $priceCurrency = $price->getCurrency(); + $currentCurrency = $this->currency(); $thatCurrency = $that->getCurrency(); - if($priceCurrency->getCurrencyCode() === $thatCurrency->getCurrencyCode()) { - return $price->getAmount()->compareTo($that->getAmount()); + if($currentCurrency->getCurrencyCode() === $thatCurrency->getCurrencyCode()) { + return $current->getAmount()->compareTo($that->getAmount()); } - throw MoneyMismatchException::currencyMismatch($priceCurrency, $thatCurrency); - } - - /** - * Transform a given value into a Money instance - */ - protected function valueToMoney(BigNumber|int|float|string|AbstractMoney|Price $value, string $method = 'inclusive'): AbstractMoney - { - if(is_a($value, Price::class)) { - $value = $value->$method(); - } - - if(is_a($value, AbstractMoney::class)) { - return $value; - } - - return Money::ofMinor($value, $this->currency()); + throw MoneyMismatchException::currencyMismatch($currentCurrency, $thatCurrency); } } \ No newline at end of file diff --git a/src/Modifier.php b/src/Modifier.php index 45240cb..6c5658c 100644 --- a/src/Modifier.php +++ b/src/Modifier.php @@ -3,7 +3,7 @@ namespace Whitecube\Price; use Brick\Money\Money; -use Brick\Money\AbstractMoney; +use Brick\Money\RationalMoney; class Modifier implements PriceAmendable { @@ -155,11 +155,11 @@ public function appliesPerUnit(): bool /** * Add an addition modification to the stack */ - public function add(...$arguments): static + public function add($argument): static { $this->stack[] = [ 'method' => 'plus', - 'arguments' => $arguments + 'argument' => $argument ]; return $this; @@ -168,11 +168,11 @@ public function add(...$arguments): static /** * Add a substraction modification to the stack */ - public function subtract(...$arguments): static + public function subtract($argument): static { $this->stack[] = [ 'method' => 'minus', - 'arguments' => $arguments + 'argument' => $argument ]; return $this; @@ -181,11 +181,11 @@ public function subtract(...$arguments): static /** * Add a multiplication modification to the stack */ - public function multiply(...$arguments): static + public function multiply($argument): static { $this->stack[] = [ 'method' => 'multipliedBy', - 'arguments' => $arguments + 'argument' => $argument ]; return $this; @@ -194,11 +194,11 @@ public function multiply(...$arguments): static /** * Add a division modification to the stack */ - public function divide(...$arguments): static + public function divide($argument): static { $this->stack[] = [ 'method' => 'dividedBy', - 'arguments' => $arguments + 'argument' => $argument ]; return $this; @@ -219,7 +219,7 @@ public function abs(): static /** * Apply the modifier on the given Money instance */ - public function apply(AbstractMoney $build, $units, $perUnit, AbstractMoney $exclusive = null, Vat $vat = null) : ?AbstractMoney + public function apply(RationalMoney $build, $units, $perUnit, RationalMoney $exclusive = null, Vat $vat = null) : ?RationalMoney { if(! $this->stack) { return null; @@ -230,15 +230,15 @@ public function apply(AbstractMoney $build, $units, $perUnit, AbstractMoney $exc return $this->applyStackAction($action, $build); } - $argument = is_a($action['arguments'][0] ?? null, AbstractMoney::class) - ? $action['arguments'][0] - : Money::ofMinor($action['arguments'][0] ?? 0, $build->getCurrency()); + $argument = is_a($action['argument'] ?? null, RationalMoney::class) + ? $action['argument'] + : Money::ofMinor($action['argument'] ?? 0, $build->getCurrency())->toRational(); if($this->appliesPerUnit() && (! $perUnit) && $units > 1) { - $argument = $argument->multipliedBy($units, Price::getRounding('exclusive')); + $argument = $argument->multipliedBy($units); } - $action['arguments'][0] = $argument; + $action['argument'] = $argument; return $this->applyStackAction($action, $build); }, $build); @@ -247,8 +247,8 @@ public function apply(AbstractMoney $build, $units, $perUnit, AbstractMoney $exc /** * Apply given stack action on the price being build */ - protected function applyStackAction(array $action, AbstractMoney $build): AbstractMoney + protected function applyStackAction(array $action, RationalMoney $build): RationalMoney { - return call_user_func_array([$build, $action['method']], $action['arguments'] ?? []); + return call_user_func([$build, $action['method']], $action['argument'] ?? null); } } \ No newline at end of file diff --git a/src/Price.php b/src/Price.php index 2c5e2aa..06b76a5 100644 --- a/src/Price.php +++ b/src/Price.php @@ -22,19 +22,24 @@ class Price implements \JsonSerializable use Concerns\HasModifiers; /** - * The rounding methods that should be used when calculating prices - * - * @var array + * The default rounding methods that should be applied when calculating prices */ - static protected array $rounding = [ - 'exclusive' => RoundingMode::HALF_UP, - 'vat' => RoundingMode::HALF_UP, - ]; + static protected ?RoundingMode $defaultRoundingMode = null; /** - * The root price + * The default Money context that should be applied transforming Rational monies */ - protected AbstractMoney $base; + static protected ?Context $defaultContext = null; + + /** + * The root pricing value + */ + protected RationalMoney $base; + + /** + * The original pricing context + */ + protected ?Context $context; /** * The VAT definition @@ -54,9 +59,14 @@ class Price implements \JsonSerializable /** * Create a new Price object */ - public function __construct(AbstractMoney $base, float|int|string $units = 1) + public function __construct(AbstractMoney $base, float|int|string $units = 1, ?Context $context = null) { - $this->base = $base; + $this->base = $this->valueToRationalMoney($base); + + $this->context = ! is_null($context) + ? $context + : (method_exists($base, 'getContext') ? $base->getContext() : null); + $this->setUnits($units); } @@ -82,19 +92,35 @@ public static function __callStatic(string $method, array $arguments): null|stri } /** - * Configure the price rounding policy + * Configure the default price rounding policy + */ + public static function setDefaultRoundingMode(RoundingMode $roundingMode): void + { + static::$defaultRoundingMode = $roundingMode; + } + + /** + * Get the default price rounding policy + */ + public static function getDefaultRoundingMode(): RoundingMode + { + return static::$defaultRoundingMode ?? RoundingMode::UNNECESSARY; + } + + /** + * Configure the default monies Context */ - public static function setRounding(string $moment, int $method): void + public static function setDefaultContext(Context $context): void { - static::$rounding[$moment] = $method; + static::$defaultContext = $context; } /** - * Get the current price rounding policy for given moment + * Get the default price rounding policy */ - public static function getRounding(string $moment): int + public static function getDefaultContext(): Context { - return static::$rounding[$moment] ?? RoundingMode::UNNECESSARY; + return static::$defaultContext ?? new DefaultContext(); } /** @@ -106,21 +132,27 @@ public function currency(): Currency } /** - * Return the price's underlying context instance + * Define the price's Money Context */ - public function context(): ?Context + public function setContext(Context $context): static { - if (! method_exists($this->base, 'getContext')) { - return null; - } + $this->context = $context; + + return $this; + } - return $this->base->getContext(); + /** + * Return the price's Money context + */ + public function context(): Context + { + return $this->context ?? static::getDefaultContext(); } /** - * Return the price's base value + * Return the price's base amount as a Rational Money instance */ - public function base(bool $perUnit = true): AbstractMoney + public function base(bool $perUnit = true): RationalMoney { return ($perUnit) ? $this->base @@ -128,9 +160,17 @@ public function base(bool $perUnit = true): AbstractMoney } /** - * Return the EXCL. Money value + * Return the price's base amount as a contextualized Money instance */ - public function exclusive(bool $perUnit = false, bool $includeAfterVat = false): AbstractMoney + public function baseMoney(bool $perUnit = true, ?RoundingMode $roundingMode = null): Money + { + return $this->toContext($this->base($perUnit), $roundingMode); + } + + /** + * Return the price's EXCL. amount as a Rational Money instance + */ + public function exclusive(bool $perUnit = false, bool $includeAfterVat = false): RationalMoney { $result = $this->build()->exclusiveBeforeVat($perUnit ? true : false); @@ -140,33 +180,60 @@ public function exclusive(bool $perUnit = false, bool $includeAfterVat = false): $supplement = $this->build()->exclusiveAfterVat($perUnit ? true : false); - return $result['amount']->plus($supplement['amount'], static::getRounding('exclusive')); + return $result['amount']->plus($supplement['amount']); } /** - * Return the INCL. Money value + * Return the price's EXCL. amount as a contextualized Money instance */ - public function inclusive(bool $perUnit = false): AbstractMoney + public function exclusiveMoney(bool $perUnit = true, bool $includeAfterVat = false, ?RoundingMode $roundingMode = null): Money + { + return $this->toContext($this->exclusive($perUnit, $includeAfterVat), $roundingMode); + } + + /** + * Return the price's INCL. amount as a Rational Money instance + */ + public function inclusive(bool $perUnit = false): RationalMoney { $result = $this->build()->inclusive($perUnit ? true : false); return $result['amount']; } + /** + * Return the price's EXCL. amount as a contextualized Money instance + */ + public function inclusiveMoney(bool $perUnit = true, ?RoundingMode $roundingMode = null): Money + { + return $this->toContext($this->inclusive($perUnit), $roundingMode); + } + + /** + * Transform the provided Rational Money to a contextualized Money instance + */ + protected function toContext(RationalMoney $value, ?RoundingMode $roundingMode = null): Money + { + return $value->to( + $this->context(), + $roundingMode ?? static::getDefaultRoundingMode() + ); + } + /** * Shorthand to easily get the underlying minor amount (as an integer) */ public function toMinor(?string $version = null): int { if ($version === 'inclusive') { - return $this->inclusive()->getMinorAmount()->toInt(); + return $this->inclusiveMoney()->getMinorAmount()->toInt(); } if ($version === 'exclusive') { - return $this->exclusive()->getMinorAmount()->toInt(); + return $this->exclusiveMoney()->getMinorAmount()->toInt(); } - return $this->base()->getMinorAmount()->toInt(); + return $this->baseMoney()->getMinorAmount()->toInt(); } /** @@ -190,13 +257,9 @@ public function perUnit(AbstractMoney $amount): AbstractMoney /** * Multiply the given amount by the number of available units */ - public function applyUnits(AbstractMoney $amount, ?int $rounding = null): AbstractMoney + public function applyUnits(RationalMoney $amount): RationalMoney { - if(is_null($rounding)) { - $rounding = static::getRounding('exclusive'); - } - - return $amount->multipliedBy($this->units, $rounding); + return $amount->multipliedBy($this->units); } /** @@ -223,41 +286,49 @@ public function build(): Calculator return $this->calculator; } + /** + * Transform a given value into a Money instance + */ + protected function valueToRationalMoney(BigNumber|int|float|string|AbstractMoney|Price $value, string $method = 'base'): RationalMoney + { + if(is_a($value, RationalMoney::class)) { + return $value; + } + + if(is_a($value, Money::class)) { + return $value->toRational(); + } + + if(is_a($value, Price::class)) { + return $value->$method(); + } + + return Money::ofMinor($value, $this->currency())->toRational(); + } + /** * Convert this price object into a readable * total & inclusive money string */ public function __toString(): string { - return $this->inclusive()->to(new DefaultContext, static::getRounding('vat'))->__toString(); + return $this->inclusiveMoney()->__toString(); } public function jsonSerialize(): array { - $excl = $this->exclusive(); - $incl = $this->inclusive(); - return [ - 'base' => $this->getRational($this->base)->getAmount(), - 'currency' => $this->base->getCurrency()->getCurrencyCode(), + 'base' => $this->base->getAmount(), + 'currency' => $this->currency()->getCurrencyCode(), 'units' => $this->units, - 'vat' => $this->vat->percentage(), + 'vat' => $this->vat?->percentage(), 'total' => [ - 'exclusive' => $this->getRational($excl)->getAmount(), - 'inclusive' => $this->getRational($incl)->getAmount(), + 'exclusive' => $this->exclusive()->getAmount(), + 'inclusive' => $this->inclusive()->getAmount(), ], ]; } - protected function getRational(AbstractMoney $money): RationalMoney - { - if(is_a($money, RationalMoney::class)) { - return $money; - } - - return $money->toRational(); - } - /** * Hydrate a price object from a json string/array * @throws \InvalidArgumentException diff --git a/src/PriceAmendable.php b/src/PriceAmendable.php index 1383447..1f4e362 100644 --- a/src/PriceAmendable.php +++ b/src/PriceAmendable.php @@ -2,7 +2,7 @@ namespace Whitecube\Price; -use Brick\Money\AbstractMoney; +use Brick\Money\RationalMoney; interface PriceAmendable { @@ -35,13 +35,6 @@ public function appliesAfterVat(): bool; /** * Apply the modifier on the given Money instance - * - * @param \Brick\Money\AbstractMoney $build - * @param float $units - * @param bool $perUnit - * @param null|\Brick\Money\AbstractMoney $exclusive - * @param null|\Whitecube\Price\Vat $vat - * @return null|\Brick\Money\AbstractMoney */ - public function apply(AbstractMoney $build, float $units, bool $perUnit, AbstractMoney $exclusive = null, Vat $vat = null) : ?AbstractMoney; + public function apply(RationalMoney $build, float $units, bool $perUnit, RationalMoney $exclusive = null, Vat $vat = null) : ?RationalMoney; } \ No newline at end of file diff --git a/src/Vat.php b/src/Vat.php index 6ac8671..b5c612f 100644 --- a/src/Vat.php +++ b/src/Vat.php @@ -4,7 +4,7 @@ use Brick\Math\BigDecimal; use Brick\Math\RoundingMode; -use Brick\Money\AbstractMoney; +use Brick\Money\RationalMoney; use Brick\Money\Money; use Whitecube\Price\Price; use Brick\Math\BigNumber; @@ -41,7 +41,7 @@ public function percentage(): float /** * Get the VAT's Money value */ - public function money(bool $perUnit = false): AbstractMoney + public function money(bool $perUnit = false): RationalMoney { return $this->price->build()->vat($perUnit); } @@ -49,10 +49,10 @@ public function money(bool $perUnit = false): AbstractMoney /** * Compute the VAT's values */ - public function apply(AbstractMoney $exclusive): AbstractMoney + public function apply(RationalMoney $exclusive): RationalMoney { $multiplier = $this->percentage->dividedBy(100, $this->percentage->getScale() + 2, RoundingMode::UP); - return $exclusive->multipliedBy($multiplier, Price::getRounding('vat')); + return $exclusive->multipliedBy($multiplier); } } diff --git a/tests/Fixtures/AfterVatAmendableModifier.php b/tests/Fixtures/AfterVatAmendableModifier.php index f1dab70..74dcca5 100644 --- a/tests/Fixtures/AfterVatAmendableModifier.php +++ b/tests/Fixtures/AfterVatAmendableModifier.php @@ -6,7 +6,7 @@ use Brick\Math\RoundingMode; use Whitecube\Price\Vat; use Whitecube\Price\PriceAmendable; -use Brick\Money\AbstractMoney; +use Brick\Money\RationalMoney; class AfterVatAmendableModifier implements PriceAmendable { @@ -62,7 +62,7 @@ public function appliesAfterVat(): bool /** * Apply the modifier on the given Money instance */ - public function apply(AbstractMoney $build, $units, $perUnit, AbstractMoney $exclusive = null, Vat $vat = null) : ?AbstractMoney + public function apply(RationalMoney $build, $units, $perUnit, RationalMoney $exclusive = null, Vat $vat = null) : ?RationalMoney { $tax = Money::ofMinor(100, 'EUR'); diff --git a/tests/Fixtures/AmendableModifier.php b/tests/Fixtures/AmendableModifier.php index a22b385..da6b697 100644 --- a/tests/Fixtures/AmendableModifier.php +++ b/tests/Fixtures/AmendableModifier.php @@ -5,7 +5,7 @@ use Brick\Math\RoundingMode; use Whitecube\Price\Vat; use Whitecube\Price\PriceAmendable; -use Brick\Money\AbstractMoney; +use Brick\Money\RationalMoney; class AmendableModifier implements PriceAmendable { @@ -61,7 +61,7 @@ public function appliesAfterVat(): bool /** * Apply the modifier on the given Money instance */ - public function apply(AbstractMoney $build, $units, $perUnit, AbstractMoney $exclusive = null, Vat $vat = null) : ?AbstractMoney + public function apply(RationalMoney $build, $units, $perUnit, RationalMoney $exclusive = null, Vat $vat = null) : ?RationalMoney { return $build->multipliedBy(1.25, RoundingMode::HALF_UP); } diff --git a/tests/Fixtures/CustomAmendableModifier.php b/tests/Fixtures/CustomAmendableModifier.php index 560b4e9..c94c094 100644 --- a/tests/Fixtures/CustomAmendableModifier.php +++ b/tests/Fixtures/CustomAmendableModifier.php @@ -6,7 +6,7 @@ use Brick\Math\RoundingMode; use Whitecube\Price\Vat; use Whitecube\Price\PriceAmendable; -use Brick\Money\AbstractMoney; +use Brick\Money\RationalMoney; class CustomAmendableModifier implements PriceAmendable { @@ -75,7 +75,7 @@ public function appliesAfterVat(): bool /** * Apply the modifier on the given Money instance */ - public function apply(AbstractMoney $build, $units, $perUnit, AbstractMoney $exclusive = null, Vat $vat = null) : ?AbstractMoney + public function apply(RationalMoney $build, $units, $perUnit, RationalMoney $exclusive = null, Vat $vat = null) : ?RationalMoney { if($perUnit) { return $build->plus($this->tax); diff --git a/tests/Unit/HasModifiersTest.php b/tests/Unit/HasModifiersTest.php index fb9bce4..96301de 100644 --- a/tests/Unit/HasModifiersTest.php +++ b/tests/Unit/HasModifiersTest.php @@ -2,6 +2,8 @@ namespace Tests\Unit; +use Brick\Math\RoundingMode; +use Brick\Money\Context\DefaultContext; use Brick\Money\Money; use Whitecube\Price\Price; use Whitecube\Price\Modifier; @@ -312,13 +314,13 @@ ->addModifier('something', CustomAmendableModifier::class, Money::ofMinor(100, 'EUR')) ->addModifier('custom', AmendableModifier::class); - expect($price->discounts()->__toString())->toBe('EUR -3.00'); - expect($price->discounts(true)->__toString())->toBe('EUR -1.50'); + expect($price->discounts()->to(new DefaultContext, RoundingMode::HALF_UP)->__toString())->toBe('EUR -3.00'); + expect($price->discounts(true)->to(new DefaultContext, RoundingMode::HALF_UP)->__toString())->toBe('EUR -1.50'); - expect($price->taxes()->__toString())->toBe('EUR 3.50'); - expect($price->taxes(true)->__toString())->toBe('EUR 1.75'); + expect($price->taxes()->to(new DefaultContext, RoundingMode::HALF_UP)->__toString())->toBe('EUR 3.50'); + expect($price->taxes(true)->to(new DefaultContext, RoundingMode::HALF_UP)->__toString())->toBe('EUR 1.75'); - expect($price->modifiers()->__toString())->toBe('EUR 7.63'); - expect($price->modifiers(true)->__toString())->toBe('EUR 3.81'); - expect($price->modifiers(false, 'custom')->__toString())->toBe('EUR 5.13'); + expect($price->modifiers()->to(new DefaultContext, RoundingMode::HALF_UP)->__toString())->toBe('EUR 7.63'); + expect($price->modifiers(true)->to(new DefaultContext, RoundingMode::HALF_UP)->__toString())->toBe('EUR 3.81'); + expect($price->modifiers(false, 'custom')->to(new DefaultContext, RoundingMode::HALF_UP)->__toString())->toBe('EUR 5.13'); });