From 5996f1e91d8b181ce3bb84a73e41143eff49d8df Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Fri, 15 Nov 2024 20:52:25 +0000 Subject: [PATCH 01/69] =?UTF-8?q?Support=20`=E2=80=94withFields`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 ++++ src/Plugin.php | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a29109fa59..2c77fc6bc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Release Notes for Craft Commerce +## Unreleased + +- Added the `--with-fields` option to all Commerce `resave/*` commands. + ## 5.2.4 - 2024-11-14 - Improved the performance of `craft\commerce\elements\Product::getVariants()`. ([#3578](https://github.com/craftcms/commerce/issues/3758)) diff --git a/src/Plugin.php b/src/Plugin.php index 3e95e4d3c6..32e0e72dad 100755 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -51,6 +51,7 @@ use craft\commerce\helpers\ProjectConfigData; use craft\commerce\linktypes\Product as ProductLinkType; use craft\commerce\migrations\Install; +use craft\commerce\models\ProductType; use craft\commerce\models\Settings; use craft\commerce\plugin\Routes; use craft\commerce\plugin\Services as CommerceServices; @@ -165,7 +166,9 @@ use craft\web\Application; use craft\web\twig\variables\CraftVariable; use Exception; +use Illuminate\Support\Collection; use yii\base\Event; +use yii\console\ExitCode; use yii\web\User; /** @@ -1143,12 +1146,32 @@ private function _defineResaveCommand(): void /** @var ResaveController $controller */ $controller = Craft::$app->controller; $criteria = []; + if ($controller->type !== null) { $criteria['type'] = explode(',', $controller->type); } + + // @TODO Remove this check when Commerce requires Craft 5.5 + if (property_exists($controller, 'withFields') && !empty($controller->withFields)) { + $handles = Collection::make(self::getInstance()->getProductTypes()->getAllProductTypes()) + ->filter(fn(ProductType $productType) => $controller->hasTheFields($productType->getFieldLayout())) + ->map(fn(ProductType $productType) => $productType->handle) + ->all(); + if (isset($criteria['type'])) { + $criteria['type'] = array_intersect($criteria['type'], $handles); + } else { + $criteria['type'] = $handles; + } + + if (empty($criteria['type'])) { + $controller->output($controller->markdownToAnsi('No product types satisfy `--with-fields`.')); + return ExitCode::UNSPECIFIED_ERROR; + } + } + return $controller->resaveElements(Product::class, $criteria); }, - 'options' => ['type'], + 'options' => array_filter(['type', (property_exists(ResaveController::class, 'withFields') ? 'withFields' : null)]), 'helpSummary' => 'Re-saves Commerce products.', 'optionsHelp' => [ 'type' => 'The product type handle(s) of the products to resave.', @@ -1159,11 +1182,20 @@ private function _defineResaveCommand(): void 'action' => function(): int { /** @var ResaveController $controller */ $controller = Craft::$app->controller; + // @TODO Remove this check when Commerce requires Craft 5.5 + if (property_exists($controller, 'withFields') && !empty($controller->withFields)) { + $fieldLayout = Craft::$app->getFields()->getLayoutByType(Order::class); + if (!$controller->hasTheFields($fieldLayout)) { + $controller->output($controller->markdownToAnsi('The order field layout doesn’t satisfy `--with-fields`.')); + return ExitCode::UNSPECIFIED_ERROR; + } + } + return $controller->resaveElements(Order::class, [ 'isCompleted' => true, ]); }, - 'options' => [], + 'options' => array_filter([(property_exists(ResaveController::class, 'withFields') ? 'withFields' : null)]), 'helpSummary' => 'Re-saves completed Commerce orders.', ]; @@ -1171,11 +1203,20 @@ private function _defineResaveCommand(): void 'action' => function(): int { /** @var ResaveController $controller */ $controller = Craft::$app->controller; + // @TODO Remove this check when Commerce requires Craft 5.5 + if (property_exists($controller, 'withFields') && !empty($controller->withFields)) { + $fieldLayout = Craft::$app->getFields()->getLayoutByType(Order::class); + if (!$controller->hasTheFields($fieldLayout)) { + $controller->output($controller->markdownToAnsi('The order field layout doesn’t satisfy `--with-fields`.')); + return ExitCode::UNSPECIFIED_ERROR; + } + } + return $controller->resaveElements(Order::class, [ 'isCompleted' => false, ]); }, - 'options' => [], + 'options' => array_filter([(property_exists(ResaveController::class, 'withFields') ? 'withFields' : null)]), 'helpSummary' => 'Re-saves Commerce carts.', ]; }); From 0d6a9bab7fc89d7e47026036aa953dbe695746aa Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Fri, 15 Nov 2024 21:04:09 +0000 Subject: [PATCH 02/69] fix phpstan --- composer.lock | 1191 +++++++++++++++++++++++++++++------------------- src/Plugin.php | 6 +- 2 files changed, 724 insertions(+), 473 deletions(-) diff --git a/composer.lock b/composer.lock index 12aed48a0a..44fc587fcd 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": "87b34df5c4b49889f8b578fd6741d6b8", + "content-hash": "2f3ba664672e378629a5fd88f697019c", "packages": [ { "name": "bacon/bacon-qr-code", @@ -186,16 +186,16 @@ }, { "name": "commerceguys/addressing", - "version": "v2.2.2", + "version": "v2.2.3", "source": { "type": "git", "url": "https://github.com/commerceguys/addressing.git", - "reference": "4d23e46ea32534268ea121453c93ceda2bc3ee69" + "reference": "ca69b9cc502867111e585f7c627894ffac6f328a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/commerceguys/addressing/zipball/4d23e46ea32534268ea121453c93ceda2bc3ee69", - "reference": "4d23e46ea32534268ea121453c93ceda2bc3ee69", + "url": "https://api.github.com/repos/commerceguys/addressing/zipball/ca69b9cc502867111e585f7c627894ffac6f328a", + "reference": "ca69b9cc502867111e585f7c627894ffac6f328a", "shasum": "" }, "require": { @@ -244,30 +244,30 @@ ], "support": { "issues": "https://github.com/commerceguys/addressing/issues", - "source": "https://github.com/commerceguys/addressing/tree/v2.2.2" + "source": "https://github.com/commerceguys/addressing/tree/v2.2.3" }, - "time": "2024-07-12T11:53:22+00:00" + "time": "2024-11-11T16:07:47+00:00" }, { "name": "composer/semver", - "version": "3.4.2", + "version": "3.4.3", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "c51258e759afdb17f1fd1fe83bc12baaef6309d6" + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/c51258e759afdb17f1fd1fe83bc12baaef6309d6", - "reference": "c51258e759afdb17f1fd1fe83bc12baaef6309d6", + "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpstan/phpstan": "^1.4", - "symfony/phpunit-bridge": "^4.2 || ^5" + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" }, "type": "library", "extra": { @@ -311,7 +311,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.2" + "source": "https://github.com/composer/semver/tree/3.4.3" }, "funding": [ { @@ -327,20 +327,20 @@ "type": "tidelift" } ], - "time": "2024-07-12T11:35:52+00:00" + "time": "2024-09-19T14:15:21+00:00" }, { "name": "craftcms/cms", - "version": "5.3.4", + "version": "5.5.0.1", "source": { "type": "git", "url": "https://github.com/craftcms/cms.git", - "reference": "6183b53d6aadab0808e673bf1deed675d9f408e4" + "reference": "5e24a0bf74ea29ea2f5c04d4c9dcc325dcbb41ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/craftcms/cms/zipball/6183b53d6aadab0808e673bf1deed675d9f408e4", - "reference": "6183b53d6aadab0808e673bf1deed675d9f408e4", + "url": "https://api.github.com/repos/craftcms/cms/zipball/5e24a0bf74ea29ea2f5c04d4c9dcc325dcbb41ba", + "reference": "5e24a0bf74ea29ea2f5c04d4c9dcc325dcbb41ba", "shasum": "" }, "require": { @@ -364,6 +364,7 @@ "ext-zip": "*", "guzzlehttp/guzzle": "^7.2.0", "illuminate/collections": "^v10.42.0", + "league/uri": "^7.0", "mikehaertl/php-shellcommand": "^1.6.3", "moneyphp/money": "^4.0", "monolog/monolog": "^3.0", @@ -382,7 +383,7 @@ "symfony/var-dumper": "^5.0|^6.0", "symfony/yaml": "^5.2.3", "theiconic/name-parser": "^1.2", - "twig/twig": "~3.8.0", + "twig/twig": "~3.14.0", "voku/stringy": "^6.4.0", "web-auth/webauthn-lib": "~4.9.0", "webonyx/graphql-php": "~14.11.5", @@ -453,7 +454,7 @@ "rss": "https://github.com/craftcms/cms/releases.atom", "source": "https://github.com/craftcms/cms" }, - "time": "2024-08-13T21:06:00+00:00" + "time": "2024-11-13T14:36:24+00:00" }, { "name": "craftcms/plugin-installer", @@ -510,16 +511,16 @@ }, { "name": "craftcms/server-check", - "version": "5.0.1", + "version": "5.0.2", "source": { "type": "git", "url": "https://github.com/craftcms/server-check.git", - "reference": "72d674834520d339006d2a32e3a59ae14b3a0ff6" + "reference": "2c0578a3b0e663402ce5bf752e7308218937fad9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/craftcms/server-check/zipball/72d674834520d339006d2a32e3a59ae14b3a0ff6", - "reference": "72d674834520d339006d2a32e3a59ae14b3a0ff6", + "url": "https://api.github.com/repos/craftcms/server-check/zipball/2c0578a3b0e663402ce5bf752e7308218937fad9", + "reference": "2c0578a3b0e663402ce5bf752e7308218937fad9", "shasum": "" }, "type": "library", @@ -548,7 +549,7 @@ "rss": "https://github.com/craftcms/server-check/releases.atom", "source": "https://github.com/craftcms/server-check" }, - "time": "2024-01-23T23:20:44+00:00" + "time": "2024-09-16T15:18:27+00:00" }, { "name": "creocoder/yii2-nested-sets", @@ -1140,20 +1141,20 @@ }, { "name": "ezyang/htmlpurifier", - "version": "v4.17.0", + "version": "v4.18.0", "source": { "type": "git", "url": "https://github.com/ezyang/htmlpurifier.git", - "reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c" + "reference": "cb56001e54359df7ae76dc522d08845dc741621b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/bbc513d79acf6691fa9cf10f192c90dd2957f18c", - "reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/cb56001e54359df7ae76dc522d08845dc741621b", + "reference": "cb56001e54359df7ae76dc522d08845dc741621b", "shasum": "" }, "require": { - "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0" + "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" }, "require-dev": { "cerdic/css-tidy": "^1.7 || ^2.0", @@ -1195,9 +1196,9 @@ ], "support": { "issues": "https://github.com/ezyang/htmlpurifier/issues", - "source": "https://github.com/ezyang/htmlpurifier/tree/v4.17.0" + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.18.0" }, - "time": "2023-11-17T15:01:25+00:00" + "time": "2024-11-01T03:51:45+00:00" }, { "name": "guzzlehttp/guzzle", @@ -1327,16 +1328,16 @@ }, { "name": "guzzlehttp/promises", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8" + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", - "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", + "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", "shasum": "" }, "require": { @@ -1390,7 +1391,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.3" + "source": "https://github.com/guzzle/promises/tree/2.0.4" }, "funding": [ { @@ -1406,7 +1407,7 @@ "type": "tidelift" } ], - "time": "2024-07-18T10:29:17+00:00" + "time": "2024-10-17T10:06:22+00:00" }, { "name": "guzzlehttp/psr7", @@ -1635,7 +1636,7 @@ }, { "name": "illuminate/collections", - "version": "v10.48.20", + "version": "v10.48.23", "source": { "type": "git", "url": "https://github.com/illuminate/collections.git", @@ -1690,7 +1691,7 @@ }, { "name": "illuminate/conditionable", - "version": "v10.48.20", + "version": "v10.48.23", "source": { "type": "git", "url": "https://github.com/illuminate/conditionable.git", @@ -1736,7 +1737,7 @@ }, { "name": "illuminate/contracts", - "version": "v10.48.20", + "version": "v10.48.23", "source": { "type": "git", "url": "https://github.com/illuminate/contracts.git", @@ -1784,7 +1785,7 @@ }, { "name": "illuminate/macroable", - "version": "v10.48.20", + "version": "v10.48.23", "source": { "type": "git", "url": "https://github.com/illuminate/macroable.git", @@ -1830,34 +1831,34 @@ }, { "name": "lcobucci/clock", - "version": "3.2.0", + "version": "3.3.1", "source": { "type": "git", "url": "https://github.com/lcobucci/clock.git", - "reference": "6f28b826ea01306b07980cb8320ab30b966cd715" + "reference": "db3713a61addfffd615b79bf0bc22f0ccc61b86b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/clock/zipball/6f28b826ea01306b07980cb8320ab30b966cd715", - "reference": "6f28b826ea01306b07980cb8320ab30b966cd715", + "url": "https://api.github.com/repos/lcobucci/clock/zipball/db3713a61addfffd615b79bf0bc22f0ccc61b86b", + "reference": "db3713a61addfffd615b79bf0bc22f0ccc61b86b", "shasum": "" }, "require": { - "php": "~8.2.0 || ~8.3.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0", "psr/clock": "^1.0" }, "provide": { "psr/clock-implementation": "1.0" }, "require-dev": { - "infection/infection": "^0.27", - "lcobucci/coding-standard": "^11.0.0", + "infection/infection": "^0.29", + "lcobucci/coding-standard": "^11.1.0", "phpstan/extension-installer": "^1.3.1", "phpstan/phpstan": "^1.10.25", "phpstan/phpstan-deprecation-rules": "^1.1.3", "phpstan/phpstan-phpunit": "^1.3.13", "phpstan/phpstan-strict-rules": "^1.5.1", - "phpunit/phpunit": "^10.2.3" + "phpunit/phpunit": "^11.3.6" }, "type": "library", "autoload": { @@ -1878,7 +1879,7 @@ "description": "Yet another clock abstraction", "support": { "issues": "https://github.com/lcobucci/clock/issues", - "source": "https://github.com/lcobucci/clock/tree/3.2.0" + "source": "https://github.com/lcobucci/clock/tree/3.3.1" }, "funding": [ { @@ -1890,7 +1891,181 @@ "type": "patreon" } ], - "time": "2023-11-17T17:00:27+00:00" + "time": "2024-09-24T20:45:14+00:00" + }, + { + "name": "league/uri", + "version": "7.4.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "bedb6e55eff0c933668addaa7efa1e1f2c417cc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/bedb6e55eff0c933668addaa7efa1e1f2c417cc4", + "reference": "bedb6e55eff0c933668addaa7efa1e1f2c417cc4", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.3", + "php": "^8.1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "league/uri-components": "Needed to easily manipulate URI objects components", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "uri-template", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.4.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-03-23T07:42:40+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.4.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "8d43ef5c841032c87e2de015972c06f3865ef718" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/8d43ef5c841032c87e2de015972c06f3865ef718", + "reference": "8d43ef5c841032c87e2de015972c06f3865ef718", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-factory": "^1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common interfaces and classes for URI representation and interaction", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.4.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-03-23T07:42:40+00:00" }, { "name": "masterminds/html5", @@ -2007,16 +2182,16 @@ }, { "name": "moneyphp/money", - "version": "v4.5.0", + "version": "v4.5.1", "source": { "type": "git", "url": "https://github.com/moneyphp/money.git", - "reference": "a1daa7daf159b4044e3d0c34c41fe2be5860e850" + "reference": "142107bec4870ac2586057dc2fe917d25c92a91e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/moneyphp/money/zipball/a1daa7daf159b4044e3d0c34c41fe2be5860e850", - "reference": "a1daa7daf159b4044e3d0c34c41fe2be5860e850", + "url": "https://api.github.com/repos/moneyphp/money/zipball/142107bec4870ac2586057dc2fe917d25c92a91e", + "reference": "142107bec4870ac2586057dc2fe917d25c92a91e", "shasum": "" }, "require": { @@ -2089,22 +2264,22 @@ ], "support": { "issues": "https://github.com/moneyphp/money/issues", - "source": "https://github.com/moneyphp/money/tree/v4.5.0" + "source": "https://github.com/moneyphp/money/tree/v4.5.1" }, - "time": "2024-02-15T19:47:21+00:00" + "time": "2024-09-27T12:04:27+00:00" }, { "name": "monolog/monolog", - "version": "3.7.0", + "version": "3.8.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "f4393b648b78a5408747de94fca38beb5f7e9ef8" + "reference": "32e515fdc02cdafbe4593e30a9350d486b125b67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f4393b648b78a5408747de94fca38beb5f7e9ef8", - "reference": "f4393b648b78a5408747de94fca38beb5f7e9ef8", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/32e515fdc02cdafbe4593e30a9350d486b125b67", + "reference": "32e515fdc02cdafbe4593e30a9350d486b125b67", "shasum": "" }, "require": { @@ -2124,12 +2299,14 @@ "guzzlehttp/psr7": "^2.2", "mongodb/mongodb": "^1.8", "php-amqplib/php-amqplib": "~2.4 || ^3", - "phpstan/phpstan": "^1.9", - "phpstan/phpstan-deprecation-rules": "^1.0", - "phpstan/phpstan-strict-rules": "^1.4", - "phpunit/phpunit": "^10.5.17", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", "predis/predis": "^1.1 || ^2", - "ruflin/elastica": "^7", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", "symfony/mailer": "^5.4 || ^6", "symfony/mime": "^5.4 || ^6" }, @@ -2180,7 +2357,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.7.0" + "source": "https://github.com/Seldaek/monolog/tree/3.8.0" }, "funding": [ { @@ -2192,28 +2369,28 @@ "type": "tidelift" } ], - "time": "2024-06-28T09:40:51+00:00" + "time": "2024-11-12T13:57:08+00:00" }, { "name": "paragonie/constant_time_encoding", - "version": "v2.7.0", + "version": "v3.0.0", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105" + "reference": "df1e7fde177501eee2037dd159cf04f5f301a512" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/52a0d99e69f56b9ec27ace92ba56897fe6993105", - "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/df1e7fde177501eee2037dd159cf04f5f301a512", + "reference": "df1e7fde177501eee2037dd159cf04f5f301a512", "shasum": "" }, "require": { - "php": "^7|^8" + "php": "^8" }, "require-dev": { - "phpunit/phpunit": "^6|^7|^8|^9", - "vimeo/psalm": "^1|^2|^3|^4" + "phpunit/phpunit": "^9", + "vimeo/psalm": "^4|^5" }, "type": "library", "autoload": { @@ -2259,7 +2436,7 @@ "issues": "https://github.com/paragonie/constant_time_encoding/issues", "source": "https://github.com/paragonie/constant_time_encoding" }, - "time": "2024-05-08T12:18:48+00:00" + "time": "2024-05-08T12:36:18+00:00" }, { "name": "paragonie/random_compat", @@ -2456,16 +2633,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.4.1", + "version": "5.6.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c" + "reference": "f3558a4c23426d12bffeaab463f8a8d8b681193c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/f3558a4c23426d12bffeaab463f8a8d8b681193c", + "reference": "f3558a4c23426d12bffeaab463f8a8d8b681193c", "shasum": "" }, "require": { @@ -2474,17 +2651,17 @@ "php": "^7.4 || ^8.0", "phpdocumentor/reflection-common": "^2.2", "phpdocumentor/type-resolver": "^1.7", - "phpstan/phpdoc-parser": "^1.7", + "phpstan/phpdoc-parser": "^1.7|^2.0", "webmozart/assert": "^1.9.1" }, "require-dev": { - "mockery/mockery": "~1.3.5", + "mockery/mockery": "~1.3.5 || ~1.6.0", "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.8", "phpstan/phpstan-mockery": "^1.1", "phpstan/phpstan-webmozart-assert": "^1.2", "phpunit/phpunit": "^9.5", - "vimeo/psalm": "^5.13" + "psalm/phar": "^5.26" }, "type": "library", "extra": { @@ -2514,29 +2691,29 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.0" }, - "time": "2024-05-21T05:55:05+00:00" + "time": "2024-11-12T11:25:25+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.8.2", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "153ae662783729388a584b4361f2545e4d841e3c" + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", - "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", "shasum": "" }, "require": { "doctrine/deprecations": "^1.0", "php": "^7.3 || ^8.0", "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" + "phpstan/phpdoc-parser": "^1.18|^2.0" }, "require-dev": { "ext-tokenizer": "*", @@ -2572,36 +2749,36 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" }, - "time": "2024-02-23T11:10:43+00:00" + "time": "2024-11-09T15:12:26+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "1.29.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4" + "reference": "c00d78fb6b29658347f9d37ebe104bffadf36299" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4", - "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/c00d78fb6b29658347f9d37ebe104bffadf36299", + "reference": "c00d78fb6b29658347f9d37ebe104bffadf36299", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": "^7.4 || ^8.0" }, "require-dev": { "doctrine/annotations": "^2.0", - "nikic/php-parser": "^4.15", + "nikic/php-parser": "^5.3.0", "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", "symfony/process": "^5.2" }, "type": "library", @@ -2619,9 +2796,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.0.0" }, - "time": "2024-05-31T08:52:43+00:00" + "time": "2024-10-13T11:29:49+00:00" }, { "name": "pixelandtonic/imagine", @@ -2686,24 +2863,24 @@ }, { "name": "pragmarx/google2fa", - "version": "v8.0.1", + "version": "v8.0.3", "source": { "type": "git", "url": "https://github.com/antonioribeiro/google2fa.git", - "reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3" + "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/80c3d801b31fe165f8fe99ea085e0a37834e1be3", - "reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3", + "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", + "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", "shasum": "" }, "require": { - "paragonie/constant_time_encoding": "^1.0|^2.0", + "paragonie/constant_time_encoding": "^1.0|^2.0|^3.0", "php": "^7.1|^8.0" }, "require-dev": { - "phpstan/phpstan": "^0.12.18", + "phpstan/phpstan": "^1.9", "phpunit/phpunit": "^7.5.15|^8.5|^9.0" }, "type": "library", @@ -2732,9 +2909,9 @@ ], "support": { "issues": "https://github.com/antonioribeiro/google2fa/issues", - "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.1" + "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.3" }, - "time": "2022-06-13T21:57:56+00:00" + "time": "2024-09-05T11:56:40+00:00" }, { "name": "pragmarx/random", @@ -3184,16 +3361,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": { @@ -3228,9 +3405,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": "psr/simple-cache", @@ -3329,24 +3506,24 @@ }, { "name": "sabberworm/php-css-parser", - "version": "v8.6.0", + "version": "v8.7.0", "source": { "type": "git", "url": "https://github.com/MyIntervals/PHP-CSS-Parser.git", - "reference": "d2fb94a9641be84d79c7548c6d39bbebba6e9a70" + "reference": "f414ff953002a9b18e3a116f5e462c56f21237cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/d2fb94a9641be84d79c7548c6d39bbebba6e9a70", - "reference": "d2fb94a9641be84d79c7548c6d39bbebba6e9a70", + "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/f414ff953002a9b18e3a116f5e462c56f21237cf", + "reference": "f414ff953002a9b18e3a116f5e462c56f21237cf", "shasum": "" }, "require": { "ext-iconv": "*", - "php": ">=5.6.20" + "php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.27" + "phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.40" }, "suggest": { "ext-mbstring": "for parsing UTF-8 CSS" @@ -3388,9 +3565,9 @@ ], "support": { "issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues", - "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.6.0" + "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.7.0" }, - "time": "2024-07-01T07:33:21+00:00" + "time": "2024-10-27T17:38:32+00:00" }, { "name": "samdark/yii2-psr-log-target", @@ -3511,16 +3688,16 @@ }, { "name": "setasign/fpdi", - "version": "v2.6.0", + "version": "v2.6.1", "source": { "type": "git", "url": "https://github.com/Setasign/FPDI.git", - "reference": "a6db878129ec6c7e141316ee71872923e7f1b7ad" + "reference": "09a816004fcee9ed3405bd164147e3fdbb79a56f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Setasign/FPDI/zipball/a6db878129ec6c7e141316ee71872923e7f1b7ad", - "reference": "a6db878129ec6c7e141316ee71872923e7f1b7ad", + "url": "https://api.github.com/repos/Setasign/FPDI/zipball/09a816004fcee9ed3405bd164147e3fdbb79a56f", + "reference": "09a816004fcee9ed3405bd164147e3fdbb79a56f", "shasum": "" }, "require": { @@ -3571,7 +3748,7 @@ ], "support": { "issues": "https://github.com/Setasign/FPDI/issues", - "source": "https://github.com/Setasign/FPDI/tree/v2.6.0" + "source": "https://github.com/Setasign/FPDI/tree/v2.6.1" }, "funding": [ { @@ -3579,7 +3756,7 @@ "type": "tidelift" } ], - "time": "2023-12-11T16:03:32+00:00" + "time": "2024-09-02T10:17:15+00:00" }, { "name": "spomky-labs/cbor-php", @@ -3844,16 +4021,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v7.1.1", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7" + "reference": "87254c78dd50721cfd015b62277a8281c5589702" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", - "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/87254c78dd50721cfd015b62277a8281c5589702", + "reference": "87254c78dd50721cfd015b62277a8281c5589702", "shasum": "" }, "require": { @@ -3904,7 +4081,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.1.1" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.1.6" }, "funding": [ { @@ -3920,7 +4097,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -4000,16 +4177,16 @@ }, { "name": "symfony/filesystem", - "version": "v6.4.9", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "b51ef8059159330b74a4d52f68e671033c0fe463" + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/b51ef8059159330b74a4d52f68e671033c0fe463", - "reference": "b51ef8059159330b74a4d52f68e671033c0fe463", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", "shasum": "" }, "require": { @@ -4046,7 +4223,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.9" + "source": "https://github.com/symfony/filesystem/tree/v6.4.13" }, "funding": [ { @@ -4062,20 +4239,20 @@ "type": "tidelift" } ], - "time": "2024-06-28T09:49:33+00:00" + "time": "2024-10-25T15:07:50+00:00" }, { "name": "symfony/http-client", - "version": "v6.4.10", + "version": "v6.4.15", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "b5e498f763e0bf5eed8dcd946e50a3b3f71d4ded" + "reference": "cb4073c905cd12b8496d24ac428a9228c1750670" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/b5e498f763e0bf5eed8dcd946e50a3b3f71d4ded", - "reference": "b5e498f763e0bf5eed8dcd946e50a3b3f71d4ded", + "url": "https://api.github.com/repos/symfony/http-client/zipball/cb4073c905cd12b8496d24ac428a9228c1750670", + "reference": "cb4073c905cd12b8496d24ac428a9228c1750670", "shasum": "" }, "require": { @@ -4139,7 +4316,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v6.4.10" + "source": "https://github.com/symfony/http-client/tree/v6.4.15" }, "funding": [ { @@ -4155,7 +4332,7 @@ "type": "tidelift" } ], - "time": "2024-07-15T09:26:24+00:00" + "time": "2024-11-13T13:40:18+00:00" }, { "name": "symfony/http-client-contracts", @@ -4237,16 +4414,16 @@ }, { "name": "symfony/mailer", - "version": "v7.1.2", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "8fcff0af9043c8f8a8e229437cea363e282f9aee" + "reference": "69c9948451fb3a6a4d47dc8261d1794734e76cdd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/8fcff0af9043c8f8a8e229437cea363e282f9aee", - "reference": "8fcff0af9043c8f8a8e229437cea363e282f9aee", + "url": "https://api.github.com/repos/symfony/mailer/zipball/69c9948451fb3a6a4d47dc8261d1794734e76cdd", + "reference": "69c9948451fb3a6a4d47dc8261d1794734e76cdd", "shasum": "" }, "require": { @@ -4297,7 +4474,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.1.2" + "source": "https://github.com/symfony/mailer/tree/v7.1.6" }, "funding": [ { @@ -4313,20 +4490,20 @@ "type": "tidelift" } ], - "time": "2024-06-28T08:00:31+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/mime", - "version": "v7.1.2", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "26a00b85477e69a4bab63b66c5dce64f18b0cbfc" + "reference": "caa1e521edb2650b8470918dfe51708c237f0598" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/26a00b85477e69a4bab63b66c5dce64f18b0cbfc", - "reference": "26a00b85477e69a4bab63b66c5dce64f18b0cbfc", + "url": "https://api.github.com/repos/symfony/mime/zipball/caa1e521edb2650b8470918dfe51708c237f0598", + "reference": "caa1e521edb2650b8470918dfe51708c237f0598", "shasum": "" }, "require": { @@ -4381,7 +4558,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.1.2" + "source": "https://github.com/symfony/mime/tree/v7.1.6" }, "funding": [ { @@ -4397,24 +4574,24 @@ "type": "tidelift" } ], - "time": "2024-06-28T10:03:55+00:00" + "time": "2024-10-25T15:11:02+00:00" }, { "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": "*" @@ -4460,7 +4637,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": [ { @@ -4476,24 +4653,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-iconv", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "c027e6a3c6aee334663ec21f5852e89738abc805" + "reference": "48becf00c920479ca2e910c22a5a39e5d47ca956" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/c027e6a3c6aee334663ec21f5852e89738abc805", - "reference": "c027e6a3c6aee334663ec21f5852e89738abc805", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/48becf00c920479ca2e910c22a5a39e5d47ca956", + "reference": "48becf00c920479ca2e910c22a5a39e5d47ca956", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-iconv": "*" @@ -4540,7 +4717,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-iconv/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-iconv/tree/v1.31.0" }, "funding": [ { @@ -4556,24 +4733,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" @@ -4618,7 +4795,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": [ { @@ -4634,26 +4811,25 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "a6e83bdeb3c84391d1dfe16f42e40727ce524a5c" + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a6e83bdeb3c84391d1dfe16f42e40727ce524a5c", - "reference": "a6e83bdeb3c84391d1dfe16f42e40727ce524a5c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773", "shasum": "" }, "require": { - "php": ">=7.1", - "symfony/polyfill-intl-normalizer": "^1.10", - "symfony/polyfill-php72": "^1.10" + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" }, "suggest": { "ext-intl": "For best performance" @@ -4702,7 +4878,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0" }, "funding": [ { @@ -4718,24 +4894,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" @@ -4783,7 +4959,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": [ { @@ -4799,24 +4975,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": "*" @@ -4863,7 +5039,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": [ { @@ -4879,40 +5055,32 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "10112722600777e02d2745716b70c5db4ca70442" + "reference": "fa2ae56c44f03bed91a39bfc9822e31e7c5c38ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/10112722600777e02d2745716b70c5db4ca70442", - "reference": "10112722600777e02d2745716b70c5db4ca70442", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/fa2ae56c44f03bed91a39bfc9822e31e7c5c38ce", + "reference": "fa2ae56c44f03bed91a39bfc9822e31e7c5c38ce", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, - "type": "library", + "type": "metapackage", "extra": { "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" - } - }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" @@ -4936,7 +5104,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-php72/tree/v1.31.0" }, "funding": [ { @@ -4952,24 +5120,24 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/polyfill-php80", - "version": "v1.30.0", + "name": "symfony/polyfill-php81", + "version": "v1.31.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "77fa7995ac1b21ab60769b7323d600a991a90433" + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433", - "reference": "77fa7995ac1b21ab60769b7323d600a991a90433", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { @@ -4983,7 +5151,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" + "Symfony\\Polyfill\\Php81\\": "" }, "classmap": [ "Resources/stubs" @@ -4994,10 +5162,6 @@ "MIT" ], "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -5007,7 +5171,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -5016,7 +5180,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0" }, "funding": [ { @@ -5032,24 +5196,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", - "reference": "2ba1f33797470debcda07fe9dce20a0003df18e9" + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/2ba1f33797470debcda07fe9dce20a0003df18e9", - "reference": "2ba1f33797470debcda07fe9dce20a0003df18e9", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-uuid": "*" @@ -5095,7 +5259,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.31.0" }, "funding": [ { @@ -5111,20 +5275,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/process", - "version": "v7.1.3", + "version": "v7.1.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca" + "reference": "42783370fda6e538771f7c7a36e9fa2ee3a84892" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/7f2f542c668ad6c313dc4a5e9c3321f733197eca", - "reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca", + "url": "https://api.github.com/repos/symfony/process/zipball/42783370fda6e538771f7c7a36e9fa2ee3a84892", + "reference": "42783370fda6e538771f7c7a36e9fa2ee3a84892", "shasum": "" }, "require": { @@ -5156,7 +5320,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.1.3" + "source": "https://github.com/symfony/process/tree/v7.1.8" }, "funding": [ { @@ -5172,20 +5336,20 @@ "type": "tidelift" } ], - "time": "2024-07-26T12:44:47+00:00" + "time": "2024-11-06T14:23:19+00:00" }, { "name": "symfony/property-access", - "version": "v7.1.1", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/property-access.git", - "reference": "74e39e6a6276b8e384f34c6ddbc10a6c9a60193a" + "reference": "975d7f7fd8fcb952364c6badc46d01a580532bf9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/74e39e6a6276b8e384f34c6ddbc10a6c9a60193a", - "reference": "74e39e6a6276b8e384f34c6ddbc10a6c9a60193a", + "url": "https://api.github.com/repos/symfony/property-access/zipball/975d7f7fd8fcb952364c6badc46d01a580532bf9", + "reference": "975d7f7fd8fcb952364c6badc46d01a580532bf9", "shasum": "" }, "require": { @@ -5232,7 +5396,7 @@ "reflection" ], "support": { - "source": "https://github.com/symfony/property-access/tree/v7.1.1" + "source": "https://github.com/symfony/property-access/tree/v7.1.6" }, "funding": [ { @@ -5248,20 +5412,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/property-info", - "version": "v7.1.3", + "version": "v7.1.8", "source": { "type": "git", "url": "https://github.com/symfony/property-info.git", - "reference": "88a279df2db5b7919cac6f35d6a5d1d7147e6a9b" + "reference": "3748f85f64351d282fd028e44309856f1d79142e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/88a279df2db5b7919cac6f35d6a5d1d7147e6a9b", - "reference": "88a279df2db5b7919cac6f35d6a5d1d7147e6a9b", + "url": "https://api.github.com/repos/symfony/property-info/zipball/3748f85f64351d282fd028e44309856f1d79142e", + "reference": "3748f85f64351d282fd028e44309856f1d79142e", "shasum": "" }, "require": { @@ -5277,7 +5441,7 @@ }, "require-dev": { "phpdocumentor/reflection-docblock": "^5.2", - "phpstan/phpdoc-parser": "^1.0", + "phpstan/phpdoc-parser": "^1.0|^2.0", "symfony/cache": "^6.4|^7.0", "symfony/dependency-injection": "^6.4|^7.0", "symfony/serializer": "^6.4|^7.0" @@ -5316,7 +5480,7 @@ "validator" ], "support": { - "source": "https://github.com/symfony/property-info/tree/v7.1.3" + "source": "https://github.com/symfony/property-info/tree/v7.1.8" }, "funding": [ { @@ -5332,20 +5496,20 @@ "type": "tidelift" } ], - "time": "2024-07-26T07:36:36+00:00" + "time": "2024-11-09T07:07:11+00:00" }, { "name": "symfony/serializer", - "version": "v6.4.10", + "version": "v6.4.15", "source": { "type": "git", "url": "https://github.com/symfony/serializer.git", - "reference": "9a67fcf320561e96f94d62bbe0e169ac534a5718" + "reference": "9d862d66198f3c2e30404228629ef4c18d5d608e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/9a67fcf320561e96f94d62bbe0e169ac534a5718", - "reference": "9a67fcf320561e96f94d62bbe0e169ac534a5718", + "url": "https://api.github.com/repos/symfony/serializer/zipball/9d862d66198f3c2e30404228629ef4c18d5d608e", + "reference": "9d862d66198f3c2e30404228629ef4c18d5d608e", "shasum": "" }, "require": { @@ -5414,7 +5578,7 @@ "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/serializer/tree/v6.4.10" + "source": "https://github.com/symfony/serializer/tree/v6.4.15" }, "funding": [ { @@ -5430,7 +5594,7 @@ "type": "tidelift" } ], - "time": "2024-07-26T13:13:26+00:00" + "time": "2024-10-23T13:25:59+00:00" }, { "name": "symfony/service-contracts", @@ -5517,16 +5681,16 @@ }, { "name": "symfony/string", - "version": "v7.1.3", + "version": "v7.1.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "ea272a882be7f20cad58d5d78c215001617b7f07" + "reference": "591ebd41565f356fcd8b090fe64dbb5878f50281" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ea272a882be7f20cad58d5d78c215001617b7f07", - "reference": "ea272a882be7f20cad58d5d78c215001617b7f07", + "url": "https://api.github.com/repos/symfony/string/zipball/591ebd41565f356fcd8b090fe64dbb5878f50281", + "reference": "591ebd41565f356fcd8b090fe64dbb5878f50281", "shasum": "" }, "require": { @@ -5584,7 +5748,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.3" + "source": "https://github.com/symfony/string/tree/v7.1.8" }, "funding": [ { @@ -5600,20 +5764,20 @@ "type": "tidelift" } ], - "time": "2024-07-22T10:25:37+00:00" + "time": "2024-11-13T13:31:21+00:00" }, { "name": "symfony/type-info", - "version": "v7.1.1", + "version": "v7.1.8", "source": { "type": "git", "url": "https://github.com/symfony/type-info.git", - "reference": "60b28eb733f1453287f1263ed305b96091e0d1dc" + "reference": "51535dde21c7abf65c9d000a30bb15f6478195e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/type-info/zipball/60b28eb733f1453287f1263ed305b96091e0d1dc", - "reference": "60b28eb733f1453287f1263ed305b96091e0d1dc", + "url": "https://api.github.com/repos/symfony/type-info/zipball/51535dde21c7abf65c9d000a30bb15f6478195e6", + "reference": "51535dde21c7abf65c9d000a30bb15f6478195e6", "shasum": "" }, "require": { @@ -5626,7 +5790,7 @@ "symfony/property-info": "<6.4" }, "require-dev": { - "phpstan/phpdoc-parser": "^1.0", + "phpstan/phpdoc-parser": "^1.0|^2.0", "symfony/dependency-injection": "^6.4|^7.0", "symfony/property-info": "^6.4|^7.0" }, @@ -5666,7 +5830,7 @@ "type" ], "support": { - "source": "https://github.com/symfony/type-info/tree/v7.1.1" + "source": "https://github.com/symfony/type-info/tree/v7.1.8" }, "funding": [ { @@ -5682,20 +5846,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:59:31+00:00" + "time": "2024-11-07T15:49:33+00:00" }, { "name": "symfony/uid", - "version": "v7.1.1", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "bb59febeecc81528ff672fad5dab7f06db8c8277" + "reference": "65befb3bb2d503bbffbd08c815aa38b472999917" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/bb59febeecc81528ff672fad5dab7f06db8c8277", - "reference": "bb59febeecc81528ff672fad5dab7f06db8c8277", + "url": "https://api.github.com/repos/symfony/uid/zipball/65befb3bb2d503bbffbd08c815aa38b472999917", + "reference": "65befb3bb2d503bbffbd08c815aa38b472999917", "shasum": "" }, "require": { @@ -5740,7 +5904,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.1.1" + "source": "https://github.com/symfony/uid/tree/v7.1.6" }, "funding": [ { @@ -5756,20 +5920,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/var-dumper", - "version": "v6.4.10", + "version": "v6.4.15", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "a71cc3374f5fb9759da1961d28c452373b343dd4" + "reference": "38254d5a5ac2e61f2b52f9caf54e7aa3c9d36b80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/a71cc3374f5fb9759da1961d28c452373b343dd4", - "reference": "a71cc3374f5fb9759da1961d28c452373b343dd4", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/38254d5a5ac2e61f2b52f9caf54e7aa3c9d36b80", + "reference": "38254d5a5ac2e61f2b52f9caf54e7aa3c9d36b80", "shasum": "" }, "require": { @@ -5825,7 +5989,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.4.10" + "source": "https://github.com/symfony/var-dumper/tree/v6.4.15" }, "funding": [ { @@ -5841,20 +6005,20 @@ "type": "tidelift" } ], - "time": "2024-07-26T12:30:32+00:00" + "time": "2024-11-08T15:28:48+00:00" }, { "name": "symfony/yaml", - "version": "v5.4.40", + "version": "v5.4.45", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "81cad0ceab3d61fe14fe941ff18a230ac9c80f83" + "reference": "a454d47278cc16a5db371fe73ae66a78a633371e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/81cad0ceab3d61fe14fe941ff18a230ac9c80f83", - "reference": "81cad0ceab3d61fe14fe941ff18a230ac9c80f83", + "url": "https://api.github.com/repos/symfony/yaml/zipball/a454d47278cc16a5db371fe73ae66a78a633371e", + "reference": "a454d47278cc16a5db371fe73ae66a78a633371e", "shasum": "" }, "require": { @@ -5900,7 +6064,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v5.4.40" + "source": "https://github.com/symfony/yaml/tree/v5.4.45" }, "funding": [ { @@ -5916,20 +6080,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:33:22+00:00" + "time": "2024-09-25T14:11:13+00:00" }, { "name": "tecnickcom/tcpdf", - "version": "6.7.5", + "version": "6.7.7", "source": { "type": "git", "url": "https://github.com/tecnickcom/TCPDF.git", - "reference": "951eabf0338ec2522bd0d5d9c79b08a3a3d36b36" + "reference": "cfbc0028cc23f057f2baf9e73bdc238153c22086" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/951eabf0338ec2522bd0d5d9c79b08a3a3d36b36", - "reference": "951eabf0338ec2522bd0d5d9c79b08a3a3d36b36", + "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/cfbc0028cc23f057f2baf9e73bdc238153c22086", + "reference": "cfbc0028cc23f057f2baf9e73bdc238153c22086", "shasum": "" }, "require": { @@ -5980,7 +6144,7 @@ ], "support": { "issues": "https://github.com/tecnickcom/TCPDF/issues", - "source": "https://github.com/tecnickcom/TCPDF/tree/6.7.5" + "source": "https://github.com/tecnickcom/TCPDF/tree/6.7.7" }, "funding": [ { @@ -5988,7 +6152,7 @@ "type": "custom" } ], - "time": "2024-04-20T17:25:10+00:00" + "time": "2024-10-26T12:15:02+00:00" }, { "name": "theiconic/name-parser", @@ -6040,30 +6204,37 @@ }, { "name": "twig/twig", - "version": "v3.8.0", + "version": "v3.14.2", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d" + "reference": "0b6f9d8370bb3b7f1ce5313ed8feb0fafd6e399a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", - "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/0b6f9d8370bb3b7f1ce5313ed8feb0fafd6e399a", + "reference": "0b6f9d8370bb3b7f1ce5313ed8feb0fafd6e399a", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.0.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php80": "^1.22" + "symfony/polyfill-php81": "^1.29" }, "require-dev": { "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0" + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" }, "type": "library", "autoload": { + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], "psr-4": { "Twig\\": "src/" } @@ -6096,7 +6267,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.8.0" + "source": "https://github.com/twigphp/Twig/tree/v3.14.2" }, "funding": [ { @@ -6108,7 +6279,7 @@ "type": "tidelift" } ], - "time": "2023-11-21T18:54:41+00:00" + "time": "2024-11-07T12:36:22+00:00" }, { "name": "voku/anti-xss", @@ -7542,25 +7713,25 @@ "packages-dev": [ { "name": "behat/gherkin", - "version": "v4.9.0", + "version": "v4.10.0", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4" + "reference": "cbb83c4c435dd8d05a161f2a5ae322e61b2f4db6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/0bc8d1e30e96183e4f36db9dc79caead300beff4", - "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/cbb83c4c435dd8d05a161f2a5ae322e61b2f4db6", + "reference": "cbb83c4c435dd8d05a161f2a5ae322e61b2f4db6", "shasum": "" }, "require": { "php": "~7.2|~8.0" }, "require-dev": { - "cucumber/cucumber": "dev-gherkin-22.0.0", + "cucumber/cucumber": "dev-gherkin-24.1.0", "phpunit/phpunit": "~8|~9", - "symfony/yaml": "~3|~4|~5" + "symfony/yaml": "~3|~4|~5|~6|~7" }, "suggest": { "symfony/yaml": "If you want to parse features, represented in YAML files" @@ -7599,9 +7770,9 @@ ], "support": { "issues": "https://github.com/Behat/Gherkin/issues", - "source": "https://github.com/Behat/Gherkin/tree/v4.9.0" + "source": "https://github.com/Behat/Gherkin/tree/v4.10.0" }, - "time": "2021-10-12T13:05:09+00:00" + "time": "2024-10-19T14:46:06+00:00" }, { "name": "codeception/codeception", @@ -7775,16 +7946,16 @@ }, { "name": "codeception/lib-innerbrowser", - "version": "4.0.3", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/Codeception/lib-innerbrowser.git", - "reference": "d9fbf1ca5297c76c44e45eaf03d44f0d4961686c" + "reference": "66b66d62b2b7f8595e257760b897190474cfb892" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/lib-innerbrowser/zipball/d9fbf1ca5297c76c44e45eaf03d44f0d4961686c", - "reference": "d9fbf1ca5297c76c44e45eaf03d44f0d4961686c", + "url": "https://api.github.com/repos/Codeception/lib-innerbrowser/zipball/66b66d62b2b7f8595e257760b897190474cfb892", + "reference": "66b66d62b2b7f8595e257760b897190474cfb892", "shasum": "" }, "require": { @@ -7828,9 +7999,9 @@ ], "support": { "issues": "https://github.com/Codeception/lib-innerbrowser/issues", - "source": "https://github.com/Codeception/lib-innerbrowser/tree/4.0.3" + "source": "https://github.com/Codeception/lib-innerbrowser/tree/4.0.5" }, - "time": "2024-02-02T20:36:15+00:00" + "time": "2024-09-13T05:08:15+00:00" }, { "name": "codeception/lib-web", @@ -8265,20 +8436,20 @@ }, { "name": "craftcms/ckeditor", - "version": "4.1.0", + "version": "4.4.0", "source": { "type": "git", "url": "https://github.com/craftcms/ckeditor.git", - "reference": "7ee8b9025b51bc907db9b9fdcc04293e0c6855d7" + "reference": "5b219fe06b395b411df0d61137fd750e834b8022" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/craftcms/ckeditor/zipball/7ee8b9025b51bc907db9b9fdcc04293e0c6855d7", - "reference": "7ee8b9025b51bc907db9b9fdcc04293e0c6855d7", + "url": "https://api.github.com/repos/craftcms/ckeditor/zipball/5b219fe06b395b411df0d61137fd750e834b8022", + "reference": "5b219fe06b395b411df0d61137fd750e834b8022", "shasum": "" }, "require": { - "craftcms/cms": "^5.2.0", + "craftcms/cms": "^5.5.0", "craftcms/html-field": "^3.1.0", "nystudio107/craft-code-editor": ">=1.0.8 <=1.0.13 || ^1.0.16", "php": "^8.2" @@ -8325,7 +8496,7 @@ "rss": "https://github.com/craftcms/ckeditor/commits/master.atom", "source": "https://github.com/craftcms/ckeditor" }, - "time": "2024-06-12T16:42:12+00:00" + "time": "2024-11-12T17:32:39+00:00" }, { "name": "craftcms/ecs", @@ -8362,16 +8533,16 @@ }, { "name": "craftcms/html-field", - "version": "3.1.0", + "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/craftcms/html-field.git", - "reference": "34a5796c74e22d4ae80709541367114d03d280ca" + "reference": "50a56e4cb4d4511b39674965e0fbff475f62f7f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/craftcms/html-field/zipball/34a5796c74e22d4ae80709541367114d03d280ca", - "reference": "34a5796c74e22d4ae80709541367114d03d280ca", + "url": "https://api.github.com/repos/craftcms/html-field/zipball/50a56e4cb4d4511b39674965e0fbff475f62f7f0", + "reference": "50a56e4cb4d4511b39674965e0fbff475f62f7f0", "shasum": "" }, "require": { @@ -8407,7 +8578,7 @@ "rss": "https://github.com/craftcms/html-field/commits/main.atom", "source": "https://github.com/craftcms/html-field" }, - "time": "2024-06-11T14:58:59+00:00" + "time": "2024-10-15T20:02:05+00:00" }, { "name": "craftcms/phpstan", @@ -8477,20 +8648,20 @@ }, { "name": "craftcms/redactor", - "version": "4.1.0", + "version": "4.2.0", "source": { "type": "git", "url": "https://github.com/craftcms/redactor.git", - "reference": "6d01f809b9feb8a4011365a2e0954d66d074bb27" + "reference": "47bc7bc40312b7d02ef5bf4fd36bd2fd62f8043c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/craftcms/redactor/zipball/6d01f809b9feb8a4011365a2e0954d66d074bb27", - "reference": "6d01f809b9feb8a4011365a2e0954d66d074bb27", + "url": "https://api.github.com/repos/craftcms/redactor/zipball/47bc7bc40312b7d02ef5bf4fd36bd2fd62f8043c", + "reference": "47bc7bc40312b7d02ef5bf4fd36bd2fd62f8043c", "shasum": "" }, "require": { - "craftcms/cms": "^5.0.0-beta.1", + "craftcms/cms": "^5.3.0", "craftcms/html-field": "^3.0.0", "php": "^8.2" }, @@ -8536,20 +8707,20 @@ "source": "https://github.com/craftcms/redactor" }, "abandoned": "craftcms/ckeditor", - "time": "2024-06-12T16:49:32+00:00" + "time": "2024-09-03T13:38:27+00:00" }, { "name": "fakerphp/faker", - "version": "v1.23.1", + "version": "v1.24.0", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b" + "reference": "a136842a532bac9ecd8a1c723852b09915d7db50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/bfb4fe148adbf78eff521199619b93a52ae3554b", - "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/a136842a532bac9ecd8a1c723852b09915d7db50", + "reference": "a136842a532bac9ecd8a1c723852b09915d7db50", "shasum": "" }, "require": { @@ -8597,9 +8768,9 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.23.1" + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.0" }, - "time": "2024-01-02T13:46:09+00:00" + "time": "2024-11-07T15:11:20+00:00" }, { "name": "graham-campbell/result-type", @@ -8873,16 +9044,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.12.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", "shasum": "" }, "require": { @@ -8921,7 +9092,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" }, "funding": [ { @@ -8929,20 +9100,20 @@ "type": "tidelift" } ], - "time": "2024-06-12T14:39:25+00:00" + "time": "2024-11-08T17:47:46+00:00" }, { "name": "nikic/php-parser", - "version": "v5.1.0", + "version": "v5.3.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", - "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", "shasum": "" }, "require": { @@ -8985,22 +9156,22 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" }, - "time": "2024-07-01T20:03:41+00:00" + "time": "2024-10-08T18:51:32+00:00" }, { "name": "nystudio107/craft-code-editor", - "version": "1.0.19", + "version": "1.0.22", "source": { "type": "git", "url": "https://github.com/nystudio107/craft-code-editor.git", - "reference": "a793406e62cd6c7d8e25ac5e0fb2208b4206815b" + "reference": "170edf71355b659e1db9ede12980b17c20eb3d1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nystudio107/craft-code-editor/zipball/a793406e62cd6c7d8e25ac5e0fb2208b4206815b", - "reference": "a793406e62cd6c7d8e25ac5e0fb2208b4206815b", + "url": "https://api.github.com/repos/nystudio107/craft-code-editor/zipball/170edf71355b659e1db9ede12980b17c20eb3d1f", + "reference": "170edf71355b659e1db9ede12980b17c20eb3d1f", "shasum": "" }, "require": { @@ -9055,7 +9226,7 @@ "type": "github" } ], - "time": "2024-04-15T16:35:48+00:00" + "time": "2024-09-23T17:20:25+00:00" }, { "name": "phar-io/manifest", @@ -9252,16 +9423,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.11.10", + "version": "1.12.10", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "640410b32995914bde3eed26fa89552f9c2c082f" + "reference": "fc463b5d0fe906dcf19689be692c65c50406a071" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/640410b32995914bde3eed26fa89552f9c2c082f", - "reference": "640410b32995914bde3eed26fa89552f9c2c082f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/fc463b5d0fe906dcf19689be692c65c50406a071", + "reference": "fc463b5d0fe906dcf19689be692c65c50406a071", "shasum": "" }, "require": { @@ -9306,39 +9477,39 @@ "type": "github" } ], - "time": "2024-08-08T09:02:50+00:00" + "time": "2024-11-11T15:37:09+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "11.0.5", + "version": "11.0.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "19b6365ab8b59a64438c0c3f4241feeb480c9861" + "reference": "f7f08030e8811582cc459871d28d6f5a1a4d35ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/19b6365ab8b59a64438c0c3f4241feeb480c9861", - "reference": "19b6365ab8b59a64438c0c3f4241feeb480c9861", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f7f08030e8811582cc459871d28d6f5a1a4d35ca", + "reference": "f7f08030e8811582cc459871d28d6f5a1a4d35ca", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^5.0", + "nikic/php-parser": "^5.3.1", "php": ">=8.2", - "phpunit/php-file-iterator": "^5.0", - "phpunit/php-text-template": "^4.0", - "sebastian/code-unit-reverse-lookup": "^4.0", - "sebastian/complexity": "^4.0", - "sebastian/environment": "^7.0", - "sebastian/lines-of-code": "^3.0", - "sebastian/version": "^5.0", - "theseer/tokenizer": "^1.2.0" + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-text-template": "^4.0.1", + "sebastian/code-unit-reverse-lookup": "^4.0.1", + "sebastian/complexity": "^4.0.1", + "sebastian/environment": "^7.2.0", + "sebastian/lines-of-code": "^3.0.1", + "sebastian/version": "^5.0.2", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.4.1" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -9347,7 +9518,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "11.0-dev" + "dev-main": "11.0.x-dev" } }, "autoload": { @@ -9376,7 +9547,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/11.0.5" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.7" }, "funding": [ { @@ -9384,20 +9555,20 @@ "type": "github" } ], - "time": "2024-07-03T05:05:37+00:00" + "time": "2024-10-09T06:21:38+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "5.0.1", + "version": "5.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "6ed896bf50bbbfe4d504a33ed5886278c78e4a26" + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6ed896bf50bbbfe4d504a33ed5886278c78e4a26", - "reference": "6ed896bf50bbbfe4d504a33ed5886278c78e4a26", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", "shasum": "" }, "require": { @@ -9437,7 +9608,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.0.1" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" }, "funding": [ { @@ -9445,7 +9616,7 @@ "type": "github" } ], - "time": "2024-07-03T05:06:37+00:00" + "time": "2024-08-27T05:02:59+00:00" }, { "name": "phpunit/php-invoker", @@ -9633,16 +9804,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.3.1", + "version": "11.4.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "fe179875ef0c14e90b75617002767eae0a742641" + "reference": "e8e8ed1854de5d36c088ec1833beae40d2dedd76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fe179875ef0c14e90b75617002767eae0a742641", - "reference": "fe179875ef0c14e90b75617002767eae0a742641", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e8e8ed1854de5d36c088ec1833beae40d2dedd76", + "reference": "e8e8ed1854de5d36c088ec1833beae40d2dedd76", "shasum": "" }, "require": { @@ -9656,21 +9827,21 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0.5", - "phpunit/php-file-iterator": "^5.0.1", + "phpunit/php-code-coverage": "^11.0.7", + "phpunit/php-file-iterator": "^5.1.0", "phpunit/php-invoker": "^5.0.1", "phpunit/php-text-template": "^4.0.1", "phpunit/php-timer": "^7.0.1", "sebastian/cli-parser": "^3.0.2", "sebastian/code-unit": "^3.0.1", - "sebastian/comparator": "^6.0.2", + "sebastian/comparator": "^6.1.1", "sebastian/diff": "^6.0.2", "sebastian/environment": "^7.2.0", "sebastian/exporter": "^6.1.3", "sebastian/global-state": "^7.0.2", "sebastian/object-enumerator": "^6.0.1", - "sebastian/type": "^5.0.1", - "sebastian/version": "^5.0.1" + "sebastian/type": "^5.1.0", + "sebastian/version": "^5.0.2" }, "suggest": { "ext-soap": "To be able to generate mocks based on WSDL files" @@ -9681,7 +9852,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "11.3-dev" + "dev-main": "11.4-dev" } }, "autoload": { @@ -9713,7 +9884,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.3.1" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.4.3" }, "funding": [ { @@ -9729,7 +9900,7 @@ "type": "tidelift" } ], - "time": "2024-08-13T06:14:23+00:00" + "time": "2024-10-28T13:07:50+00:00" }, { "name": "psy/psysh", @@ -9812,21 +9983,21 @@ }, { "name": "rector/rector", - "version": "1.2.3", + "version": "1.2.10", "source": { "type": "git", "url": "https://github.com/rectorphp/rector.git", - "reference": "2433e95410aef1b34b15d7f1b6a134365a4ddb39" + "reference": "40f9cf38c05296bd32f444121336a521a293fa61" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/2433e95410aef1b34b15d7f1b6a134365a4ddb39", - "reference": "2433e95410aef1b34b15d7f1b6a134365a4ddb39", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/40f9cf38c05296bd32f444121336a521a293fa61", + "reference": "40f9cf38c05296bd32f444121336a521a293fa61", "shasum": "" }, "require": { "php": "^7.2|^8.0", - "phpstan/phpstan": "^1.11.9" + "phpstan/phpstan": "^1.12.5" }, "conflict": { "rector/rector-doctrine": "*", @@ -9859,7 +10030,7 @@ ], "support": { "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/1.2.3" + "source": "https://github.com/rectorphp/rector/tree/1.2.10" }, "funding": [ { @@ -9867,7 +10038,7 @@ "type": "github" } ], - "time": "2024-08-12T16:36:46+00:00" + "time": "2024-11-08T13:59:10+00:00" }, { "name": "sebastian/cli-parser", @@ -10041,16 +10212,16 @@ }, { "name": "sebastian/comparator", - "version": "6.0.2", + "version": "6.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "450d8f237bd611c45b5acf0733ce43e6bb280f81" + "reference": "43d129d6a0f81c78bee378b46688293eb7ea3739" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/450d8f237bd611c45b5acf0733ce43e6bb280f81", - "reference": "450d8f237bd611c45b5acf0733ce43e6bb280f81", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/43d129d6a0f81c78bee378b46688293eb7ea3739", + "reference": "43d129d6a0f81c78bee378b46688293eb7ea3739", "shasum": "" }, "require": { @@ -10061,12 +10232,12 @@ "sebastian/exporter": "^6.0" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.4" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "6.2-dev" } }, "autoload": { @@ -10106,7 +10277,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/6.0.2" + "source": "https://github.com/sebastianbergmann/comparator/tree/6.2.1" }, "funding": [ { @@ -10114,7 +10285,7 @@ "type": "github" } ], - "time": "2024-08-12T06:07:25+00:00" + "time": "2024-10-31T05:30:08+00:00" }, { "name": "sebastian/complexity", @@ -10683,28 +10854,28 @@ }, { "name": "sebastian/type", - "version": "5.0.1", + "version": "5.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "fb6a6566f9589e86661291d13eba708cce5eb4aa" + "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb6a6566f9589e86661291d13eba708cce5eb4aa", - "reference": "fb6a6566f9589e86661291d13eba708cce5eb4aa", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/461b9c5da241511a2a0e8f240814fb23ce5c0aac", + "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -10728,7 +10899,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/type/issues", "security": "https://github.com/sebastianbergmann/type/security/policy", - "source": "https://github.com/sebastianbergmann/type/tree/5.0.1" + "source": "https://github.com/sebastianbergmann/type/tree/5.1.0" }, "funding": [ { @@ -10736,20 +10907,20 @@ "type": "github" } ], - "time": "2024-07-03T05:11:49+00:00" + "time": "2024-09-17T13:12:04+00:00" }, { "name": "sebastian/version", - "version": "5.0.1", + "version": "5.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "45c9debb7d039ce9b97de2f749c2cf5832a06ac4" + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/45c9debb7d039ce9b97de2f749c2cf5832a06ac4", - "reference": "45c9debb7d039ce9b97de2f749c2cf5832a06ac4", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", "shasum": "" }, "require": { @@ -10782,7 +10953,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/version/issues", "security": "https://github.com/sebastianbergmann/version/security/policy", - "source": "https://github.com/sebastianbergmann/version/tree/5.0.1" + "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" }, "funding": [ { @@ -10790,7 +10961,7 @@ "type": "github" } ], - "time": "2024-07-03T05:13:08+00:00" + "time": "2024-10-09T05:16:32+00:00" }, { "name": "softcreatr/jsonpath", @@ -10863,16 +11034,16 @@ }, { "name": "symfony/browser-kit", - "version": "v7.1.1", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "9c13742e3175b5815e272b981876ae329bec2040" + "reference": "714becc9ba9b20115ffededc58f6b7172dc394cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/9c13742e3175b5815e272b981876ae329bec2040", - "reference": "9c13742e3175b5815e272b981876ae329bec2040", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/714becc9ba9b20115ffededc58f6b7172dc394cf", + "reference": "714becc9ba9b20115ffededc58f6b7172dc394cf", "shasum": "" }, "require": { @@ -10911,7 +11082,7 @@ "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/browser-kit/tree/v7.1.1" + "source": "https://github.com/symfony/browser-kit/tree/v7.1.6" }, "funding": [ { @@ -10927,20 +11098,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-10-25T15:11:02+00:00" }, { "name": "symfony/console", - "version": "v7.1.3", + "version": "v7.1.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9" + "reference": "ff04e5b5ba043d2badfb308197b9e6b42883fcd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9", - "reference": "cb1dcb30ebc7005c29864ee78adb47b5fb7c3cd9", + "url": "https://api.github.com/repos/symfony/console/zipball/ff04e5b5ba043d2badfb308197b9e6b42883fcd5", + "reference": "ff04e5b5ba043d2badfb308197b9e6b42883fcd5", "shasum": "" }, "require": { @@ -11004,7 +11175,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.3" + "source": "https://github.com/symfony/console/tree/v7.1.8" }, "funding": [ { @@ -11020,20 +11191,20 @@ "type": "tidelift" } ], - "time": "2024-07-26T12:41:01+00:00" + "time": "2024-11-06T14:23:19+00:00" }, { "name": "symfony/css-selector", - "version": "v7.1.1", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "1c7cee86c6f812896af54434f8ce29c8d94f9ff4" + "reference": "4aa4f6b3d6749c14d3aa815eef8226632e7bbc66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/1c7cee86c6f812896af54434f8ce29c8d94f9ff4", - "reference": "1c7cee86c6f812896af54434f8ce29c8d94f9ff4", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/4aa4f6b3d6749c14d3aa815eef8226632e7bbc66", + "reference": "4aa4f6b3d6749c14d3aa815eef8226632e7bbc66", "shasum": "" }, "require": { @@ -11069,7 +11240,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v7.1.1" + "source": "https://github.com/symfony/css-selector/tree/v7.1.6" }, "funding": [ { @@ -11085,20 +11256,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/dom-crawler", - "version": "v7.1.1", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "01ce8174447f1f1dd33a5854b01beef79061d9fa" + "reference": "794ddd5481ba15d8a04132c95e211cd5656e09fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/01ce8174447f1f1dd33a5854b01beef79061d9fa", - "reference": "01ce8174447f1f1dd33a5854b01beef79061d9fa", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/794ddd5481ba15d8a04132c95e211cd5656e09fb", + "reference": "794ddd5481ba15d8a04132c95e211cd5656e09fb", "shasum": "" }, "require": { @@ -11136,7 +11307,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v7.1.1" + "source": "https://github.com/symfony/dom-crawler/tree/v7.1.6" }, "funding": [ { @@ -11152,20 +11323,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-10-25T15:11:02+00:00" }, { "name": "symfony/finder", - "version": "v7.1.3", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "717c6329886f32dc65e27461f80f2a465412fdca" + "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/717c6329886f32dc65e27461f80f2a465412fdca", - "reference": "717c6329886f32dc65e27461f80f2a465412fdca", + "url": "https://api.github.com/repos/symfony/finder/zipball/2cb89664897be33f78c65d3d2845954c8d7a43b8", + "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8", "shasum": "" }, "require": { @@ -11200,7 +11371,87 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.1.3" + "source": "https://github.com/symfony/finder/tree/v7.1.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-01T08:31:23+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" }, "funding": [ { @@ -11216,7 +11467,7 @@ "type": "tidelift" } ], - "time": "2024-07-24T07:08:44+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symplify/easy-coding-standard", diff --git a/src/Plugin.php b/src/Plugin.php index 32e0e72dad..4e5522ebaa 100755 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -1152,7 +1152,7 @@ private function _defineResaveCommand(): void } // @TODO Remove this check when Commerce requires Craft 5.5 - if (property_exists($controller, 'withFields') && !empty($controller->withFields)) { + if (version_compare(Craft::$app->getInfo()->version, '5.5.0', '>=') && !empty($controller->withFields)) { $handles = Collection::make(self::getInstance()->getProductTypes()->getAllProductTypes()) ->filter(fn(ProductType $productType) => $controller->hasTheFields($productType->getFieldLayout())) ->map(fn(ProductType $productType) => $productType->handle) @@ -1183,7 +1183,7 @@ private function _defineResaveCommand(): void /** @var ResaveController $controller */ $controller = Craft::$app->controller; // @TODO Remove this check when Commerce requires Craft 5.5 - if (property_exists($controller, 'withFields') && !empty($controller->withFields)) { + if (version_compare(Craft::$app->getInfo()->version, '5.5.0', '>=') && !empty($controller->withFields)) { $fieldLayout = Craft::$app->getFields()->getLayoutByType(Order::class); if (!$controller->hasTheFields($fieldLayout)) { $controller->output($controller->markdownToAnsi('The order field layout doesn’t satisfy `--with-fields`.')); @@ -1204,7 +1204,7 @@ private function _defineResaveCommand(): void /** @var ResaveController $controller */ $controller = Craft::$app->controller; // @TODO Remove this check when Commerce requires Craft 5.5 - if (property_exists($controller, 'withFields') && !empty($controller->withFields)) { + if (version_compare(Craft::$app->getInfo()->version, '5.5.0', '>=') && !empty($controller->withFields)) { $fieldLayout = Craft::$app->getFields()->getLayoutByType(Order::class); if (!$controller->hasTheFields($fieldLayout)) { $controller->output($controller->markdownToAnsi('The order field layout doesn’t satisfy `--with-fields`.')); From 9f0e2be042ebe8bf59bcc63146262525185475ea Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Mon, 18 Nov 2024 16:53:30 +0000 Subject: [PATCH 03/69] Add coupon code condition rule and element query param --- CHANGELOG-WIP.md | 12 ++++- .../orders/CouponCodeConditionRule.php | 29 +++++++++++ .../conditions/orders/OrderCondition.php | 3 +- src/elements/db/OrderQuery.php | 52 +++++++++++++++++++ tests/unit/elements/order/OrderQueryTest.php | 39 ++++++++++++++ 5 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 src/elements/conditions/orders/CouponCodeConditionRule.php diff --git a/CHANGELOG-WIP.md b/CHANGELOG-WIP.md index de991b6ce5..ce8be4ab44 100644 --- a/CHANGELOG-WIP.md +++ b/CHANGELOG-WIP.md @@ -1,5 +1,13 @@ # Release Notes for Craft Commerce (WIP) -## Administration +### Administration +- Added a new "Coupon Code" order condition rule. ([#3776](https://github.com/craftcms/commerce/discussions/3776)) +- Added a new "Payment Gateway" order condition rule. ([#3722](https://github.com/craftcms/commerce/discussions/3722)) -- Added a new "Payment Gateway" order condition rule. ([#3722](https://github.com/craftcms/commerce/discussions/3722)) \ No newline at end of file +### Development +- Added the `couponCode` order query param. + +### Extensibility +- Added `craft\commerce\elements\conditions\orders\CouponCodeConditionRule`. +- Added `craft\commerce\elements\db\OrderQuery::$couponCode`. +- Added `craft\commerce\elements\db\OrderQuery::couponCode()`. \ No newline at end of file diff --git a/src/elements/conditions/orders/CouponCodeConditionRule.php b/src/elements/conditions/orders/CouponCodeConditionRule.php new file mode 100644 index 0000000000..e865619e48 --- /dev/null +++ b/src/elements/conditions/orders/CouponCodeConditionRule.php @@ -0,0 +1,29 @@ + + * @since 5.3.0 + */ +class CouponCodeConditionRule extends OrderTextValuesAttributeConditionRule +{ + public string $orderAttribute = 'couponCode'; + + /** + * @inheritdoc + */ + public function getLabel(): string + { + return Craft::t('commerce', 'Coupon Code'); + } +} diff --git a/src/elements/conditions/orders/OrderCondition.php b/src/elements/conditions/orders/OrderCondition.php index dd9e894c1c..15330cc8d0 100644 --- a/src/elements/conditions/orders/OrderCondition.php +++ b/src/elements/conditions/orders/OrderCondition.php @@ -30,8 +30,9 @@ protected function selectableConditionRules(): array { return array_merge(parent::selectableConditionRules(), [ DateOrderedConditionRule::class, - CustomerConditionRule::class, CompletedConditionRule::class, + CouponCodeConditionRule::class, + CustomerConditionRule::class, PaidConditionRule::class, HasPurchasableConditionRule::class, ItemSubtotalConditionRule::class, diff --git a/src/elements/db/OrderQuery.php b/src/elements/db/OrderQuery.php index a48d3b2626..cb1794e4a8 100644 --- a/src/elements/db/OrderQuery.php +++ b/src/elements/db/OrderQuery.php @@ -59,6 +59,12 @@ class OrderQuery extends ElementQuery */ public mixed $reference = null; + /** + * @var mixed The order reference of the resulting order. + * @used-by couponCode() + */ + public mixed $couponCode = null; + /** * @var mixed The email address the resulting orders must have. */ @@ -372,6 +378,48 @@ public function reference(mixed $value): OrderQuery return $this; } + /** + * Narrows the query results based on the order's coupon code. + * + * Possible values include: + * + * | Value | Fetches {elements}… + * | - | - + * | `':empty:'` | that don’t have a coupon code. + * | `':notempty:'` | that have a coupon code. + * | `'Foo'` | with a coupon code of `Foo`. + * | `'Foo*'` | with a coupon code that begins with `Foo`. + * | `'*Foo'` | with a coupon code that ends with `Foo`. + * | `'*Foo*'` | with a coupon code that contains `Foo`. + * | `'not *Foo*'` | with a coupon code that doesn’t contain `Foo`. + * | `['*Foo*', '*Bar*']` | with a coupon code that contains `Foo` or `Bar`. + * | `['not', '*Foo*', '*Bar*']` | with a coupon code that doesn’t contain `Foo` or `Bar`. + * + * --- + * + * ```twig + * {# Fetch the requested {element} #} + * {% set {element-var} = {twig-method} + * .reference('foo') + * .one() %} + * ``` + * + * ```php + * // Fetch the requested {element} + * ${element-var} = {php-method} + * ->reference('foo') + * ->one(); + * ``` + * + * @param string|null $value The property value + * @return static self reference + */ + public function couponCode(mixed $value): OrderQuery + { + $this->couponCode = $value; + return $this; + } + /** * Narrows the query results based on the customers’ email addresses. * @@ -1603,6 +1651,10 @@ protected function beforePrepare(): bool $this->subQuery->andWhere(Db::parseParam('commerce_orders.reference', $this->reference)); } + if (isset($this->couponCode)) { + $this->subQuery->andWhere(Db::parseParam('commerce_orders.couponCode', $this->couponCode)); + } + if (isset($this->email) && $this->email) { // Join and search the users table for email address $this->subQuery->leftJoin(CraftTable::USERS . ' users', '[[users.id]] = [[commerce_orders.customerId]]'); diff --git a/tests/unit/elements/order/OrderQueryTest.php b/tests/unit/elements/order/OrderQueryTest.php index 2c06a75e6c..9c6720864e 100644 --- a/tests/unit/elements/order/OrderQueryTest.php +++ b/tests/unit/elements/order/OrderQueryTest.php @@ -63,6 +63,45 @@ public function emailDataProvider(): array ]; } + /** + * @param string $couponCode + * @param int $count + * @return void + * @dataProvider couponCodeDataProvider + */ + public function testCouponCode(?string $couponCode, int $count): void + { + $ordersFixture = $this->tester->grabFixture('orders'); + /** @var Order $order */ + $order = $ordersFixture->getElement('completed-new'); + + // Temporarily add a coupon code to an order + \craft\commerce\records\Order::updateAll(['couponCode' => 'foo'], ['id' => $order->id]); + + $orderQuery = Order::find(); + $orderQuery->couponCode($couponCode); + + self::assertCount($count, $orderQuery->all()); + + // Remove temporary coupon code + \craft\commerce\records\Order::updateAll(['couponCode' => null], ['id' => $order->id]); + } + + /** + * @return array[] + */ + public function couponCodeDataProvider(): array + { + return [ + 'normal' => ['foo', 1], + 'case-insensitive' => ['fOo', 1], + 'using-null' => [null, 3], + 'empty-code' => [':empty:', 2], + 'not-empty-code' => [':notempty:', 1], + 'no-results' => ['nope', 0], + ]; + } + /** * @param mixed $handle * @param int $count From ebf639bd45569fe7b5e9a3ad2a259e193ac47efd Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Tue, 19 Nov 2024 09:05:00 +0000 Subject: [PATCH 04/69] Fix order query coupon code, should be case-insensitive --- src/elements/db/OrderQuery.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/elements/db/OrderQuery.php b/src/elements/db/OrderQuery.php index cb1794e4a8..18de59bf54 100644 --- a/src/elements/db/OrderQuery.php +++ b/src/elements/db/OrderQuery.php @@ -1652,7 +1652,8 @@ protected function beforePrepare(): bool } if (isset($this->couponCode)) { - $this->subQuery->andWhere(Db::parseParam('commerce_orders.couponCode', $this->couponCode)); + // Coupon code criteria is case-insensitive like in the adjuster + $this->subQuery->andWhere(Db::parseParam('commerce_orders.couponCode', $this->couponCode, caseInsensitive: true)); } if (isset($this->email) && $this->email) { From 664fc0f2fb6daf6566b66556da5dd0f7fec4c7fc Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Tue, 19 Nov 2024 14:18:42 +0000 Subject: [PATCH 05/69] `CouponCodeConditionRule` re-implements `matchValue()` method to be case insensitive --- .../orders/CouponCodeConditionRule.php | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/elements/conditions/orders/CouponCodeConditionRule.php b/src/elements/conditions/orders/CouponCodeConditionRule.php index e865619e48..a6c231899f 100644 --- a/src/elements/conditions/orders/CouponCodeConditionRule.php +++ b/src/elements/conditions/orders/CouponCodeConditionRule.php @@ -8,6 +8,8 @@ namespace craft\commerce\elements\conditions\orders; use Craft; +use craft\helpers\StringHelper; +use yii\base\InvalidConfigException; /** * Order Coupon Code condition rule. @@ -26,4 +28,30 @@ public function getLabel(): string { return Craft::t('commerce', 'Coupon Code'); } + + /** + * @inheritdoc + */ + protected function matchValue(mixed $value): bool + { + switch ($this->operator) { + case self::OPERATOR_EMPTY: + return !$value; + case self::OPERATOR_NOT_EMPTY: + return (bool)$value; + } + + if ($this->value === '') { + return true; + } + + return match ($this->operator) { + self::OPERATOR_EQ => strcasecmp($value, $this->value) === 0, + self::OPERATOR_NE => strcasecmp($value, $this->value) !== 0, + self::OPERATOR_BEGINS_WITH => is_string($value) && StringHelper::startsWith($value, $this->value, false), + self::OPERATOR_ENDS_WITH => is_string($value) && StringHelper::endsWith($value, $this->value, false), + self::OPERATOR_CONTAINS => is_string($value) && StringHelper::contains($value, $this->value, false), + default => throw new InvalidConfigException("Invalid operator: $this->operator"), + }; + } } From f3720592497ca56db38455ba609f18c00d3d9a76 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Tue, 19 Nov 2024 14:20:28 +0000 Subject: [PATCH 06/69] =?UTF-8?q?Make=20sure=20the=20discount=20order=20co?= =?UTF-8?q?ndition=20doesn=E2=80=99t=20have=20the=20coupon=20code=20rule?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/elements/conditions/orders/DiscountOrderCondition.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/elements/conditions/orders/DiscountOrderCondition.php b/src/elements/conditions/orders/DiscountOrderCondition.php index 14c7a730ad..4b35288076 100644 --- a/src/elements/conditions/orders/DiscountOrderCondition.php +++ b/src/elements/conditions/orders/DiscountOrderCondition.php @@ -5,6 +5,7 @@ use craft\commerce\base\HasStoreInterface; use craft\commerce\base\StoreTrait; use craft\elements\db\ElementQueryInterface; +use craft\helpers\ArrayHelper; use yii\base\NotSupportedException; /** @@ -41,7 +42,12 @@ protected function config(): array */ protected function selectableConditionRules(): array { - return array_merge(parent::selectableConditionRules(), []); + $rules = array_merge(parent::selectableConditionRules(), []); + + // We don't need the condition to have the coupon code rule + ArrayHelper::removeValue($rules, CouponCodeConditionRule::class); + + return $rules; } /** From 597f93a77f02143c72025795eae8a86b83ea92cc Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Tue, 19 Nov 2024 14:27:06 +0000 Subject: [PATCH 07/69] Add `CouponCodeConditionRule` to `OrderConditionTest` --- tests/unit/elements/order/conditions/OrderConditionTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/unit/elements/order/conditions/OrderConditionTest.php b/tests/unit/elements/order/conditions/OrderConditionTest.php index b040a3cc97..fd5cdec51f 100644 --- a/tests/unit/elements/order/conditions/OrderConditionTest.php +++ b/tests/unit/elements/order/conditions/OrderConditionTest.php @@ -9,6 +9,7 @@ use Codeception\Test\Unit; use craft\commerce\elements\conditions\orders\CompletedConditionRule; +use craft\commerce\elements\conditions\orders\CouponCodeConditionRule; use craft\commerce\elements\conditions\orders\CustomerConditionRule; use craft\commerce\elements\conditions\orders\DateOrderedConditionRule; use craft\commerce\elements\conditions\orders\HasPurchasableConditionRule; @@ -66,8 +67,9 @@ public function testConditionRuleTypes(): void $rules = array_keys($rules); self::assertContains(DateOrderedConditionRule::class, $rules); - self::assertContains(CustomerConditionRule::class, $rules); self::assertContains(CompletedConditionRule::class, $rules); + self::assertContains(CouponCodeConditionRule::class, $rules); + self::assertContains(CustomerConditionRule::class, $rules); self::assertContains(PaidConditionRule::class, $rules); self::assertContains(HasPurchasableConditionRule::class, $rules); self::assertContains(ItemSubtotalConditionRule::class, $rules); From 609551c66d12cf0ce43eab16823fac95dbf8bfaa Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Tue, 19 Nov 2024 14:27:21 +0000 Subject: [PATCH 08/69] Add `CouponCodeConditionRuleTest` --- .../CouponCodeConditionRuleTest.php | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 tests/unit/elements/order/conditions/CouponCodeConditionRuleTest.php diff --git a/tests/unit/elements/order/conditions/CouponCodeConditionRuleTest.php b/tests/unit/elements/order/conditions/CouponCodeConditionRuleTest.php new file mode 100644 index 0000000000..1905ef5bb6 --- /dev/null +++ b/tests/unit/elements/order/conditions/CouponCodeConditionRuleTest.php @@ -0,0 +1,162 @@ + + * @since 5.3.0 + */ +class CouponCodeConditionRuleTest extends Unit +{ + /** + * @return array + */ + public function _fixtures(): array + { + return [ + 'orders' => [ + 'class' => OrdersFixture::class, + ], + ]; + } + + /** + * @group Order + * @dataProvider matchElementDataProvider + */ + public function testMatchElement(?string $coupon, string $operator = '=', ?string $orderCoupon = null, bool $expectedMatch = true): void + { + $condition = $this->_createCondition($coupon, $operator); + + $ordersFixture = $this->tester->grabFixture('orders'); + /** @var Order $order */ + $order = $ordersFixture->getElement('completed-new'); + + if ($orderCoupon) { + $order->couponCode = $orderCoupon; + } + + $match = $condition->matchElement($order); + + if ($expectedMatch) { + self::assertTrue($match); + } else { + self::assertFalse($match); + } + } + + /** + * @return array[] + */ + public function matchElementDataProvider(): array + { + return [ + 'match-equals' => ['coupon1', '=', 'coupon1', true], + 'match-equals-case-insensitive' => ['coupon1', '=', 'cOuPoN1', true], + 'no-match-equals' => ['coupon1', '=', 'coupon2', false], + 'no-match-equals-case-insensitive' => ['coupon1', '=', 'cOuPoN2', false], + 'no-match-equals-null' => ['coupon1', '=', null, false], + 'match-contains' => ['coupon1', '**', 'coupon1', true], + 'match-contains-case-insensitive' => ['coupon1', '**', 'cOuPoN1', true], + 'no-match-contains' => ['coupon1', '**', 'coupon2', false], + 'no-match-contains-case-insensitive' => ['coupon1', '**', 'cOuPoN2', false], + 'match-begins-with' => ['coupon', 'bw', 'coupon1', true], + 'match-begins-with-case-insensitive' => ['coupon', 'bw', 'cOuPoN1', true], + 'no-match-begins-with' => ['coupon', 'bw', 'foocoupon2', false], + 'no-match-begins-with-case-insensitive' => ['coupon', 'bw', 'foocOuPoN2', false], + 'match-ends-with' => ['pon1', 'ew', 'coupon1', true], + 'match-ends-with-case-insensitive' => ['pon1', 'ew', 'cOuPoN1', true], + 'no-match-ends-with' => ['pon2', 'ew', 'coupon2foo', false], + 'no-match-ends-with-case-insensitive' => ['pon2', 'ew', 'cOuPoN2foo', false], + ]; + } + + /** + * @group Order + * @dataProvider modifyQueryDataProvider + */ + public function testModifyQuery(?string $coupon, string $operator = '=', ?string $orderCoupon = null, int $expectedResults = 0): void + { + $condition = $this->_createCondition($coupon, $operator); + $orderFixture = $this->tester->grabFixture('orders'); + /** @var Order $order */ + $order = $orderFixture->getElement('completed-new'); + + // Temporarily add a coupon code to an order + \craft\commerce\records\Order::updateAll(['couponCode' => $orderCoupon], ['id' => $order->id]); + + $query = Order::find(); + $condition->modifyQuery($query); + + self::assertCount($expectedResults, $query->ids()); + + if ($expectedResults > 0) { + self::assertContainsEquals($order->id, $query->ids()); + } else { + self::assertEmpty($query->ids()); + } + + // Remove temporary coupon code + \craft\commerce\records\Order::updateAll(['couponCode' => null], ['id' => $order->id]); + } + + /** + * @return array[] + */ + public function modifyQueryDataProvider(): array + { + return [ + 'match-equals' => ['coupon1', '=', 'coupon1', 1], + 'match-equals-case-insensitive' => ['coupon1', '=', 'cOuPoN1', 1], + 'no-match-equals' => ['coupon1', '=', 'coupon2', 0], + 'no-match-equals-case-insensitive' => ['coupon1', '=', 'cOuPoN2', 0], + 'no-match-equals-null' => ['coupon1', '=', null, 0], + 'match-contains' => ['coupon1', '**', 'coupon1', 1], + 'match-contains-case-insensitive' => ['coupon1', '**', 'cOuPoN1', 1], + 'no-match-contains' => ['coupon1', '**', 'coupon2', 0], + 'no-match-contains-case-insensitive' => ['coupon1', '**', 'cOuPoN2', 0], + 'match-begins-with' => ['coupon', 'bw', 'coupon1', 1], + 'match-begins-with-case-insensitive' => ['coupon', 'bw', 'cOuPoN1', 1], + 'no-match-begins-with' => ['coupon', 'bw', 'foocoupon2', 0], + 'no-match-begins-with-case-insensitive' => ['coupon', 'bw', 'foocOuPoN2', 0], + 'match-ends-with' => ['pon1', 'ew', 'coupon1', 1], + 'match-ends-with-case-insensitive' => ['pon1', 'ew', 'cOuPoN1', 1], + 'no-match-ends-with' => ['pon2', 'ew', 'coupon2foo', 0], + 'no-match-ends-with-case-insensitive' => ['pon2', 'ew', 'cOuPoN2foo', 0], + ]; + } + + /** + * @param string|null $value + * @param string|null $operator + * @return OrderCondition + */ + private function _createCondition(?string $value, ?string $operator = null): OrderCondition + { + $condition = Order::createCondition(); + /** @var CouponCodeConditionRule $rule */ + $rule = \Craft::$app->getConditions()->createConditionRule(CouponCodeConditionRule::class); + $rule->value = $value; + + if ($operator) { + $rule->operator = $operator; + } + + $condition->addConditionRule($rule); + + return $condition; + } +} From ba34da1b82138641ce86559bb005d365a788d3a2 Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Wed, 20 Nov 2024 15:41:29 +0800 Subject: [PATCH 09/69] Possible fix for #3778 --- src/migrations/m240308_133451_tidy_shipping_categories.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/migrations/m240308_133451_tidy_shipping_categories.php b/src/migrations/m240308_133451_tidy_shipping_categories.php index e4b99e486c..38fd250bd1 100644 --- a/src/migrations/m240308_133451_tidy_shipping_categories.php +++ b/src/migrations/m240308_133451_tidy_shipping_categories.php @@ -25,9 +25,9 @@ public function safeUp(): bool $this->dropForeignKeyIfExists(Table::SHIPPINGCATEGORIES, ['storeId']); - $this->addForeignKey(null, Table::SHIPPINGCATEGORIES, ['storeId'], Table::STORES, ['id'], 'CASCADE'); - $this->alterColumn(Table::SHIPPINGCATEGORIES, 'storeId', $this->integer()->notNull()); + + $this->addForeignKey(null, Table::SHIPPINGCATEGORIES, ['storeId'], Table::STORES, ['id'], 'CASCADE'); return true; } From 74386859f34237b6e5c31cdefb1ac0c638fb6b44 Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Wed, 20 Nov 2024 17:22:32 +0800 Subject: [PATCH 10/69] Fixed possible #3778 --- src/migrations/m240313_131445_tidy_shipping_methods.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/migrations/m240313_131445_tidy_shipping_methods.php b/src/migrations/m240313_131445_tidy_shipping_methods.php index a9a0c0d3bd..01746a7a83 100644 --- a/src/migrations/m240313_131445_tidy_shipping_methods.php +++ b/src/migrations/m240313_131445_tidy_shipping_methods.php @@ -25,10 +25,10 @@ public function safeUp(): bool $this->dropForeignKeyIfExists(Table::SHIPPINGMETHODS, ['storeId']); - $this->addForeignKey(null, Table::SHIPPINGMETHODS, ['storeId'], Table::STORES, ['id'], 'CASCADE'); - $this->alterColumn(Table::SHIPPINGMETHODS, 'storeId', $this->integer()->notNull()); + $this->addForeignKey(null, Table::SHIPPINGMETHODS, ['storeId'], Table::STORES, ['id'], 'CASCADE'); + return true; } From 03cebf15e63f05d184161f74ce9606489b152f01 Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Wed, 20 Nov 2024 21:36:13 +0800 Subject: [PATCH 11/69] Release notes --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a29109fa59..cb324196b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Release Notes for Craft Commerce +## Unreleased + +- Fixed a SQL error that could occur when updating. ([#3778](https://github.com/craftcms/commerce/issues/3778)) + ## 5.2.4 - 2024-11-14 - Improved the performance of `craft\commerce\elements\Product::getVariants()`. ([#3578](https://github.com/craftcms/commerce/issues/3758)) From fe76bc1736c9ebe0d3d1329e3ff47715c99356e0 Mon Sep 17 00:00:00 2001 From: brandonkelly Date: Wed, 20 Nov 2024 06:46:48 -0800 Subject: [PATCH 12/69] Changelog tweak [ci skip] --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62d9823956..b3d8f23c29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -- Added the `--with-fields` option to all Commerce `resave/*` commands. +- The `resave/products`, `resave/orders`, and `resave/carts` commands now support the `--with-fields` option. - Fixed a SQL error that could occur when updating. ([#3778](https://github.com/craftcms/commerce/issues/3778)) ## 5.2.4 - 2024-11-14 From 2cf8bb893fb57bbb81347c3343b9039da71379ff Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Wed, 20 Nov 2024 23:00:20 +0800 Subject: [PATCH 13/69] Finish 5.2.5 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3d8f23c29..913d9dcb86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Release Notes for Craft Commerce -## Unreleased +## 5.2.5 - 2024-11-20 - The `resave/products`, `resave/orders`, and `resave/carts` commands now support the `--with-fields` option. - Fixed a SQL error that could occur when updating. ([#3778](https://github.com/craftcms/commerce/issues/3778)) From fd7140650baa5cb6922022cf1c527b7224daeb37 Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Wed, 20 Nov 2024 23:01:29 +0800 Subject: [PATCH 14/69] Replaces #3772 --- src/elements/Product.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/elements/Product.php b/src/elements/Product.php index 20526d32c8..5a9478381a 100644 --- a/src/elements/Product.php +++ b/src/elements/Product.php @@ -1065,6 +1065,18 @@ public function getVariants(bool $includeDisabled = false): VariantCollection return $this->_variants->filter(fn(Variant $variant) => $includeDisabled || ($variant->getStatus() === self::STATUS_ENABLED)); } + /** + * @return VariantCollection + * @throws InvalidConfigException + * @internal Do not use. Temporary method until we get a nested element manager provider in core. + * + * TODO: Remove this once we have a nested element manager provider interface in core. + */ + public function getAllVariants(): VariantCollection + { + return $this->getVariants(true); + } + /** * @inheritdoc */ @@ -1187,9 +1199,9 @@ public function getVariantManager(): NestedElementManager /** @phpstan-ignore-next-line */ fn(Product $product) => self::createVariantQuery($product), [ - 'attribute' => 'variants', + 'attribute' => 'allVariants', // TODO: can change this back to 'variants' once we have a nested element manager provider in core. 'propagationMethod' => $this->getType()->propagationMethod, - 'valueGetter' => fn(Product $product) => $product->getVariants(true), + 'valueSetter' => fn($variants) => $this->setVariants($variants), // TODO: can change this back to 'variants' once we have a nested element manager provider in core. ], ); } From 09054df7748a609b2579bb51d370aeafa9432a0c Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Wed, 20 Nov 2024 23:02:50 +0800 Subject: [PATCH 15/69] Cleanup --- src/elements/Product.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/elements/Product.php b/src/elements/Product.php index 5a9478381a..b8acf1a1e0 100644 --- a/src/elements/Product.php +++ b/src/elements/Product.php @@ -1069,7 +1069,7 @@ public function getVariants(bool $includeDisabled = false): VariantCollection * @return VariantCollection * @throws InvalidConfigException * @internal Do not use. Temporary method until we get a nested element manager provider in core. - * + * * TODO: Remove this once we have a nested element manager provider interface in core. */ public function getAllVariants(): VariantCollection From 714f74f18f66b2d01d422dad0f0f916ee3dfa9bc Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Thu, 21 Nov 2024 15:10:06 +0000 Subject: [PATCH 16/69] Bump Craft requirement --- composer.json | 2 +- composer.lock | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/composer.json b/composer.json index b3b4d39ca0..871b90968a 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "prefer-stable": true, "require": { "php": "^8.2", - "craftcms/cms": "^5.2.0", + "craftcms/cms": "^5.5.0", "dompdf/dompdf": "^2.0.2", "ibericode/vat": "^1.2.2", "iio/libmergepdf": "^4.0", diff --git a/composer.lock b/composer.lock index 44fc587fcd..a56e134c3b 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": "2f3ba664672e378629a5fd88f697019c", + "content-hash": "733b4b868ad8dbf52e6ea739a7a83494", "packages": [ { "name": "bacon/bacon-qr-code", @@ -331,16 +331,16 @@ }, { "name": "craftcms/cms", - "version": "5.5.0.1", + "version": "5.5.1.1", "source": { "type": "git", "url": "https://github.com/craftcms/cms.git", - "reference": "5e24a0bf74ea29ea2f5c04d4c9dcc325dcbb41ba" + "reference": "2233b27fd7e80cccc3aab927ad073f5916167dba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/craftcms/cms/zipball/5e24a0bf74ea29ea2f5c04d4c9dcc325dcbb41ba", - "reference": "5e24a0bf74ea29ea2f5c04d4c9dcc325dcbb41ba", + "url": "https://api.github.com/repos/craftcms/cms/zipball/2233b27fd7e80cccc3aab927ad073f5916167dba", + "reference": "2233b27fd7e80cccc3aab927ad073f5916167dba", "shasum": "" }, "require": { @@ -454,7 +454,7 @@ "rss": "https://github.com/craftcms/cms/releases.atom", "source": "https://github.com/craftcms/cms" }, - "time": "2024-11-13T14:36:24+00:00" + "time": "2024-11-19T02:11:31+00:00" }, { "name": "craftcms/plugin-installer", @@ -9423,16 +9423,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.10", + "version": "1.12.11", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "fc463b5d0fe906dcf19689be692c65c50406a071" + "reference": "0d1fc20a962a91be578bcfe7cf939e6e1a2ff733" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/fc463b5d0fe906dcf19689be692c65c50406a071", - "reference": "fc463b5d0fe906dcf19689be692c65c50406a071", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/0d1fc20a962a91be578bcfe7cf939e6e1a2ff733", + "reference": "0d1fc20a962a91be578bcfe7cf939e6e1a2ff733", "shasum": "" }, "require": { @@ -9477,7 +9477,7 @@ "type": "github" } ], - "time": "2024-11-11T15:37:09+00:00" + "time": "2024-11-17T14:08:01+00:00" }, { "name": "phpunit/php-code-coverage", From 242086009513fefcf0193aa6fb22534fb735f044 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Thu, 21 Nov 2024 15:10:29 +0000 Subject: [PATCH 17/69] WIP Card layout designer --- src/elements/Product.php | 28 +++++++++++++++++++ src/services/LineItems.php | 1 + .../settings/producttypes/_edit.twig | 2 ++ 3 files changed, 31 insertions(+) diff --git a/src/elements/Product.php b/src/elements/Product.php index 20526d32c8..ee853418b6 100644 --- a/src/elements/Product.php +++ b/src/elements/Product.php @@ -565,6 +565,34 @@ protected static function defineDefaultTableAttributes(string $source): array return $attributes; } + /** + * @inheritdoc + */ + protected static function defineCardAttributes(): array + { + return array_merge(parent::defineCardAttributes(), [ + 'defaultPrice' => [ + 'label' => Craft::t('commerce', 'Price'), + 'placeholder' => '¤' . Craft::$app->getFormattingLocale()->getFormatter()->asDecimal(123.99), + ], + 'defaultSku' => [ + 'label' => Craft::t('commerce', 'SKU'), + 'placeholder' => Html::tag('code', 'SKU123'), + ], + ]); + } + + /** + * @inheritdoc + */ + protected static function defineDefaultCardAttributes(): array + { + return array_merge(parent::defineDefaultCardAttributes(), [ + 'defaultSku', + 'defaultPrice', + ]); + } + /** * @inheritdoc */ diff --git a/src/services/LineItems.php b/src/services/LineItems.php index e7627fde24..812c1994cd 100644 --- a/src/services/LineItems.php +++ b/src/services/LineItems.php @@ -444,6 +444,7 @@ public function create(Order $order, array $params = [], LineItemType $type = Li } $params['class'] = LineItem::class; + /** @var LineItem $lineItem */ $lineItem = Craft::createObject($params); if ($lineItem->type === LineItemType::Purchasable) { diff --git a/src/templates/settings/producttypes/_edit.twig b/src/templates/settings/producttypes/_edit.twig index a290e42450..d607cb77d1 100644 --- a/src/templates/settings/producttypes/_edit.twig +++ b/src/templates/settings/producttypes/_edit.twig @@ -408,6 +408,7 @@ {{ forms.fieldLayoutDesignerField({ fieldLayout: productType.getProductFieldLayout(), + withCardViewDesigner: true, }) }} @@ -417,6 +418,7 @@ {% namespace "variant-layout" %} {{ forms.fieldLayoutDesignerField({ fieldLayout: productType.getVariantFieldLayout(), + withCardViewDesigner: true, }) }} {% endnamespace %} From 7ca145f376c732ce03981baf7f4c7dcafadb855a Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Thu, 21 Nov 2024 15:11:06 +0000 Subject: [PATCH 18/69] Changelog --- CHANGELOG-WIP.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG-WIP.md b/CHANGELOG-WIP.md index de991b6ce5..7608192b6f 100644 --- a/CHANGELOG-WIP.md +++ b/CHANGELOG-WIP.md @@ -1,5 +1,7 @@ # Release Notes for Craft Commerce (WIP) -## Administration +### Administration +- Added a new "Payment Gateway" order condition rule. ([#3722](https://github.com/craftcms/commerce/discussions/3722)) -- Added a new "Payment Gateway" order condition rule. ([#3722](https://github.com/craftcms/commerce/discussions/3722)) \ No newline at end of file +### System +- Craft Commerce now requires Craft CMS 5.5 or later. \ No newline at end of file From badedede48c270f9094a1489743afb39f59f4bc6 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Thu, 21 Nov 2024 15:14:30 +0000 Subject: [PATCH 19/69] Fixed #3768 inline editing variant prices with localized formatting --- CHANGELOG.md | 4 ++++ src/base/Purchasable.php | 21 ++++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 913d9dcb86..8030019c56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Release Notes for Craft Commerce +## Unreleased + +- Fixed a bug where prices could display incorrectly when inline editing a variant. ([#3768](https://github.com/craftcms/commerce/issues/3768)) + ## 5.2.5 - 2024-11-20 - The `resave/products`, `resave/orders`, and `resave/carts` commands now support the `--with-fields` option. diff --git a/src/base/Purchasable.php b/src/base/Purchasable.php index dcae3e164f..ce7906982c 100644 --- a/src/base/Purchasable.php +++ b/src/base/Purchasable.php @@ -329,15 +329,30 @@ public function setAttributesFromRequest(array $values): void */ protected function inlineAttributeInputHtml(string $attribute): string { + $localizePrice = function(string $attribute) { + $price = $this->{$attribute}; + if (empty($this->getErrors($attribute))) { + if ($price === null && $attribute === 'basePromotionalPrice') { + return null; + } elseif ($price === null) { + $price = 0; + } + + $price = Craft::$app->getFormatter()->asDecimal($price); + } + + return $price; + }; + return match ($attribute) { 'availableForPurchase' => PurchasableHelper::availableForPurchaseInputHtml($this->availableForPurchase), - 'price' => Currency::moneyInputHtml($this->basePrice, [ + 'price' => Currency::moneyInputHtml($localizePrice('basePrice'), [ 'id' => 'base-price', 'name' => 'basePrice', 'currency' => $this->getStore()->getCurrency()->getCode(), 'currencyLabel' => $this->getStore()->getCurrency()->getCode(), ]), - 'promotionalPrice' => Currency::moneyInputHtml($this->basePromotionalPrice, [ + 'promotionalPrice' => Currency::moneyInputHtml($localizePrice('basePromotionalPrice'), [ 'id' => 'base-promotional-price', 'name' => 'basePromotionalPrice', 'currency' => $this->getStore()->getCurrency()->getCode(), @@ -1262,7 +1277,7 @@ protected function attributeHtml(string $attribute): string return match ($attribute) { 'sku' => (string)Html::encode($this->getSkuAsText()), 'price' => $this->basePriceAsCurrency, - 'promotionalPrice' => $this->basePromotionalPriceAsCurrency, + 'promotionalPrice' => $this->basePromotionalPrice !== null ? $this->basePromotionalPriceAsCurrency : '', 'weight' => $this->weight !== null ? Craft::$app->getFormattingLocale()->getFormatter()->asDecimal($this->$attribute) . ' ' . Plugin::getInstance()->getSettings()->weightUnits : '', 'length' => $this->length !== null ? Craft::$app->getFormattingLocale()->getFormatter()->asDecimal($this->$attribute) . ' ' . Plugin::getInstance()->getSettings()->dimensionUnits : '', 'width' => $this->width !== null ? Craft::$app->getFormattingLocale()->getFormatter()->asDecimal($this->$attribute) . ' ' . Plugin::getInstance()->getSettings()->dimensionUnits : '', From a02a5d331c5cbf62860cd3799c672c60aab682c0 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Tue, 26 Nov 2024 08:09:29 +0000 Subject: [PATCH 20/69] Improve variant query performance #3758 --- CHANGELOG.md | 3 ++- src/elements/db/PurchasableQuery.php | 4 ++-- src/fieldlayoutelements/PurchasableStockField.php | 6 ++++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8030019c56..d71279e1c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ ## Unreleased -- Fixed a bug where prices could display incorrectly when inline editing a variant. ([#3768](https://github.com/craftcms/commerce/issues/3768)) +- Fixed a bug where variant prices could display incorrectly when inline editing. ([#3768](https://github.com/craftcms/commerce/issues/3768)) +- Fixed a performance degradation bug with variant queries. ([#3758](https://github.com/craftcms/commerce/issues/3758)) ## 5.2.5 - 2024-11-20 diff --git a/src/elements/db/PurchasableQuery.php b/src/elements/db/PurchasableQuery.php index 9f054c08da..bbda2206c6 100755 --- a/src/elements/db/PurchasableQuery.php +++ b/src/elements/db/PurchasableQuery.php @@ -670,7 +670,7 @@ protected function afterPrepare(): bool $this->subQuery->leftJoin(['purchasables_stores' => Table::PURCHASABLES_STORES], '[[purchasables_stores.storeId]] = [[sitestores.storeId]] AND [[purchasables_stores.purchasableId]] = [[commerce_purchasables.id]]'); $this->subQuery->leftJoin(['catalogprices' => $catalogPricesQuery], '[[catalogprices.purchasableId]] = [[commerce_purchasables.id]] AND [[catalogprices.storeId]] = [[sitestores.storeId]]'); - $this->subQuery->leftJoin(['inventoryitems' => Table::INVENTORYITEMS], '[[inventoryitems.purchasableId]] = [[commerce_purchasables.id]] OR [[inventoryitems.purchasableId]] = [[elements.canonicalId]]'); + $this->subQuery->leftJoin(['inventoryitems' => Table::INVENTORYITEMS], '[[inventoryitems.purchasableId]] = [[commerce_purchasables.id]]'); return parent::afterPrepare(); } @@ -705,7 +705,7 @@ protected function beforePrepare(): bool $this->query->leftJoin(Table::SITESTORES . ' sitestores', '[[elements_sites.siteId]] = [[sitestores.siteId]]'); $this->query->leftJoin(Table::PURCHASABLES_STORES . ' purchasables_stores', '[[purchasables_stores.storeId]] = [[sitestores.storeId]] AND [[purchasables_stores.purchasableId]] = [[commerce_purchasables.id]]'); - $this->query->leftJoin(['inventoryitems' => Table::INVENTORYITEMS], '[[inventoryitems.purchasableId]] = [[commerce_purchasables.id]] OR [[inventoryitems.purchasableId]] = [[elements.canonicalId]]'); + $this->query->leftJoin(['inventoryitems' => Table::INVENTORYITEMS], '[[inventoryitems.purchasableId]] = [[commerce_purchasables.id]]'); $this->subQuery->addSelect([ 'catalogprices.price', diff --git a/src/fieldlayoutelements/PurchasableStockField.php b/src/fieldlayoutelements/PurchasableStockField.php index f6f4e7159e..eeb28f0b8d 100644 --- a/src/fieldlayoutelements/PurchasableStockField.php +++ b/src/fieldlayoutelements/PurchasableStockField.php @@ -52,6 +52,12 @@ class PurchasableStockField extends BaseNativeField */ public function inputHtml(ElementInterface $element = null, bool $static = false): ?string { + // If this is a revision get the canonical element to show the stock for. + // @TODO re-evaluate this when we have a better way to handle revisions and inventory. + if ($element->getIsRevision()) { + $element = $element->getCanonical(); + } + $view = Craft::$app->getView(); $view->registerAssetBundle(InventoryAsset::class); From 16100ff9bc709b5ffb49c0ba7589bb72dcecd90c Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Tue, 26 Nov 2024 09:07:08 +0000 Subject: [PATCH 21/69] #3780 store handle not being passed to store management templates --- CHANGELOG.md | 1 + src/controllers/BaseStoreManagementController.php | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d71279e1c4..8085af9999 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Fixed a bug where variant prices could display incorrectly when inline editing. ([#3768](https://github.com/craftcms/commerce/issues/3768)) - Fixed a performance degradation bug with variant queries. ([#3758](https://github.com/craftcms/commerce/issues/3758)) +- Fixed a PHP error that could occur when managing store settings. ([#3780](https://github.com/craftcms/commerce/issues/3780)) ## 5.2.5 - 2024-11-20 diff --git a/src/controllers/BaseStoreManagementController.php b/src/controllers/BaseStoreManagementController.php index f0185773a4..c713f5211e 100644 --- a/src/controllers/BaseStoreManagementController.php +++ b/src/controllers/BaseStoreManagementController.php @@ -40,6 +40,16 @@ public function init(): void public function renderTemplate(string $template, array $variables = [], ?string $templateMode = null): YiiResponse { $variables['storeSettingsNav'] = $this->getStoreSettingsNav(); + + if (!isset($variables['storeHandle'])) { + $routeParams = Craft::$app->getUrlManager()->getRouteParams(); + + // Make sure store handle is always passed to the template + if (isset($routeParams['storeHandle'])) { + $variables['storeHandle'] = $routeParams['storeHandle']; + } + } + return parent::renderTemplate($template, $variables, $templateMode); } From 7cea0eb0021c661644ca5f8272187829c8b64d4e Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Tue, 26 Nov 2024 09:16:25 +0000 Subject: [PATCH 22/69] PHPstan fix --- src/controllers/BaseStoreManagementController.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/controllers/BaseStoreManagementController.php b/src/controllers/BaseStoreManagementController.php index c713f5211e..2a34a6c4ea 100644 --- a/src/controllers/BaseStoreManagementController.php +++ b/src/controllers/BaseStoreManagementController.php @@ -9,6 +9,7 @@ use Craft; use craft\commerce\Plugin; +use craft\web\UrlManager; use yii\base\InvalidConfigException; use yii\web\Response as YiiResponse; @@ -42,7 +43,9 @@ public function renderTemplate(string $template, array $variables = [], ?string $variables['storeSettingsNav'] = $this->getStoreSettingsNav(); if (!isset($variables['storeHandle'])) { - $routeParams = Craft::$app->getUrlManager()->getRouteParams(); + /** @var UrlManager $urlManager */ + $urlManager = Craft::$app->getUrlManager(); + $routeParams = $urlManager->getRouteParams(); // Make sure store handle is always passed to the template if (isset($routeParams['storeHandle'])) { From 5c2b3323c3cb1652bfd5fee57f0a39f15757bbea Mon Sep 17 00:00:00 2001 From: Brandon Kelly Date: Tue, 26 Nov 2024 02:12:32 -0800 Subject: [PATCH 23/69] Changelog tweak --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8085af9999..4fb28ea570 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -- Fixed a bug where variant prices could display incorrectly when inline editing. ([#3768](https://github.com/craftcms/commerce/issues/3768)) +- Fixed a bug where variant prices could be displayed incorrectly when inline editing. ([#3768](https://github.com/craftcms/commerce/issues/3768)) - Fixed a performance degradation bug with variant queries. ([#3758](https://github.com/craftcms/commerce/issues/3758)) - Fixed a PHP error that could occur when managing store settings. ([#3780](https://github.com/craftcms/commerce/issues/3780)) From 0e3d2fc0870d774d2879434fb2dfed9d91964c88 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Tue, 26 Nov 2024 10:18:58 +0000 Subject: [PATCH 24/69] Finish 5.2.6 [ci skip] --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fb28ea570..20b74c0148 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Release Notes for Craft Commerce -## Unreleased +## 5.2.6 - 2024-11-26 - Fixed a bug where variant prices could be displayed incorrectly when inline editing. ([#3768](https://github.com/craftcms/commerce/issues/3768)) - Fixed a performance degradation bug with variant queries. ([#3758](https://github.com/craftcms/commerce/issues/3758)) From 7e560a6bfbbc3afd3bcb9f21bb324a178de587f6 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Wed, 27 Nov 2024 19:22:06 +0000 Subject: [PATCH 25/69] Fixed permission checks for the commerce screen on edit user --- CHANGELOG.md | 4 ++++ src/Plugin.php | 5 ++++- src/controllers/UsersController.php | 26 +++++++++++++++----------- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20b74c0148..0f492d723f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Release Notes for Craft Commerce +## Unreleased + +- Fixed a bug where users could access the Commerce user screen when the current user didn’t have permission. + ## 5.2.6 - 2024-11-26 - Fixed a bug where variant prices could be displayed incorrectly when inline editing. ([#3768](https://github.com/craftcms/commerce/issues/3768)) diff --git a/src/Plugin.php b/src/Plugin.php index 4e5522ebaa..e8764107b6 100755 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -797,7 +797,10 @@ function(DefineBehaviorsEvent $event) { // Add Commerce info to user edit screen Event::on(UsersController::class, UsersController::EVENT_DEFINE_EDIT_SCREENS, function(DefineEditUserScreensEvent $event) { - $event->screens[CommerceUsersController::SCREEN_COMMERCE] = ['label' => Craft::t('commerce', 'Commerce')]; + // Add Commerce screen to user edit screen if the user has permission to access Commerce + if (Craft::$app->getUser()->checkPermission('accessPlugin-commerce')) { + $event->screens[CommerceUsersController::SCREEN_COMMERCE] = ['label' => Craft::t('commerce', 'Commerce')]; + } }); // Site models are instantiated early meaning we have to manually attach the behavior alongside using the event diff --git a/src/controllers/UsersController.php b/src/controllers/UsersController.php index 71a5063227..812ace7bdf 100644 --- a/src/controllers/UsersController.php +++ b/src/controllers/UsersController.php @@ -63,20 +63,23 @@ public function actionIndex(?int $userId = null): Response $edge = Plugin::getInstance()->getCarts()->getActiveCartEdgeDuration(); - $content = Html::tag('h2', Craft::t('commerce', 'Orders')) . - Html::beginTag('div', ['class' => 'commerce-user-orders']) . + $content = ''; + + if (Craft::$app->getUser()->getIdentity()->can('commerce-manageOrders')) { + $content .= Html::tag('h2', Craft::t('commerce', 'Orders')) . + Html::beginTag('div', ['class' => 'commerce-user-orders']) . Cp::elementIndexHtml(Order::class, ArrayHelper::merge($config, [ 'id' => sprintf('element-index-%s', mt_rand()), 'jsSettings' => [ 'criteria' => ['isCompleted' => true], ], ])) . - Html::endTag('div') . + Html::endTag('div') . - Html::tag('hr') . + Html::tag('hr') . - Html::tag('h2', Craft::t('commerce', 'Active Carts')) . - Html::beginTag('div', ['class' => 'commerce-user-active-carts']) . + Html::tag('h2', Craft::t('commerce', 'Active Carts')) . + Html::beginTag('div', ['class' => 'commerce-user-active-carts']) . Cp::elementIndexHtml(Order::class, ArrayHelper::merge($config, [ 'id' => sprintf('element-index-%s', mt_rand()), 'jsSettings' => [ @@ -86,12 +89,12 @@ public function actionIndex(?int $userId = null): Response ], ], ])) . - Html::endTag('div') . + Html::endTag('div') . - Html::tag('hr') . + Html::tag('hr') . - Html::tag('h2', Craft::t('commerce', 'Inactive Carts')) . - Html::beginTag('div', ['class' => 'commerce-user-active-carts']) . + Html::tag('h2', Craft::t('commerce', 'Inactive Carts')) . + Html::beginTag('div', ['class' => 'commerce-user-active-carts']) . Cp::elementIndexHtml(Order::class, ArrayHelper::merge($config, [ 'id' => sprintf('element-index-%s', mt_rand()), 'jsSettings' => [ @@ -101,7 +104,8 @@ public function actionIndex(?int $userId = null): Response ], ], ])) . - Html::endTag('div'); + Html::endTag('div'); + } if (Craft::$app->getUser()->getIdentity()->can('commerce-manageSubscriptions') and !empty(Plugin::getInstance()->getPlans()->getAllPlans())) { From 92fc28fbd3b0350de7e34a11350bb96db1de089e Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Thu, 28 Nov 2024 17:21:05 +0000 Subject: [PATCH 26/69] WIP variant card attributes --- src/elements/Product.php | 11 ++++++ src/elements/Variant.php | 76 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/src/elements/Product.php b/src/elements/Product.php index ee853418b6..74dc8174e6 100644 --- a/src/elements/Product.php +++ b/src/elements/Product.php @@ -565,6 +565,17 @@ protected static function defineDefaultTableAttributes(string $source): array return $attributes; } + /** + * @inheritdoc + */ + public static function attributePreviewHtml(array $attribute): mixed + { + return match($attribute['value']) { + 'defaultSku' => $attribute['placeholder'], + default => parent::attributePreviewHtml($attribute) + }; + } + /** * @inheritdoc */ diff --git a/src/elements/Variant.php b/src/elements/Variant.php index f0ddc22b28..065f60015a 100755 --- a/src/elements/Variant.php +++ b/src/elements/Variant.php @@ -1329,6 +1329,73 @@ protected static function defineActions(string $source): array ]]; } + /** + * @inheritdoc + */ + public static function attributePreviewHtml(array $attribute): mixed + { + return match($attribute['value']) { + 'sku', 'priceView' => $attribute['placeholder'], + 'availableForPurchase', 'promotable' => Html::tag('span', '', [ + 'class' => 'checkbox-icon', + 'role' => 'img', + 'title' => $attribute['label'], + 'aria' => [ + 'label' => $attribute['label'], + ], + ]) . + Html::tag('span', $attribute['label'], [ + 'class' => 'checkbox-preview-label', + ]), + default => parent::attributePreviewHtml($attribute) + }; + } + + /** + * @inheritdoc + */ + protected static function defineCardAttributes(): array + { + return array_merge(parent::defineCardAttributes(), [ + 'basePromotionalPrice' => [ + 'label' => Craft::t('commerce', 'Base Promotional Price'), + 'placeholder' => '¤' . Craft::$app->getFormattingLocale()->getFormatter()->asDecimal(123.99), + ], + 'basePrice' => [ + 'label' => Craft::t('commerce', 'Base Price'), + 'placeholder' => '¤' . Craft::$app->getFormattingLocale()->getFormatter()->asDecimal(123.99), + ], + 'product' => [ + 'label' => Craft::t('commerce', 'Product'), + ], + 'promotable' => [ + 'label' => Craft::t('commerce', 'Promotable'), + ], + 'availableForPurchase' => [ + 'label' => Craft::t('commerce', 'Available for purchase'), + ], + 'priceView' => [ + 'label' => Craft::t('commerce', 'Price'), + 'placeholder' => Html::tag('del','¤' . Craft::$app->getFormattingLocale()->getFormatter()->asDecimal(199.99), ['style' => 'opacity: .5']) . ' ¤' . Craft::$app->getFormattingLocale()->getFormatter()->asDecimal(123.99), + ], + 'sku' => [ + 'label' => Craft::t('commerce', 'SKU'), + 'placeholder' => Html::tag('code', 'SKU123'), + ], + ]); + } + + /** + * @inheritdoc + */ + protected static function defineDefaultCardAttributes(): array + { + return array_merge(parent::defineDefaultCardAttributes(), [ + 'sku', + 'priceView', + ]); + } + /** * @inheritdoc */ @@ -1377,6 +1444,15 @@ protected function attributeHtml(string $attribute): string return sprintf(' %s', $product->getStatus(), Html::encode($product->title)); } + if ($attribute === 'priceView') { + $price = $this->basePriceAsCurrency; + if ($this->getBasePromotionalPrice() && $this->getBasePromotionalPrice() < $this->getBasePrice()) { + $price = Html::tag('del', $price, ['style' => 'opacity: .5']) . ' ' . $this->basePromotionalPriceAsCurrency; + } + + return $price; + } + return parent::attributeHtml($attribute); } } From 2d4ae2ecaf75f41f3c15d192cdcede21a0e33a3f Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Thu, 28 Nov 2024 17:57:16 +0000 Subject: [PATCH 27/69] =?UTF-8?q?#3785=20Product=20type=E2=80=99s=20max=20?= =?UTF-8?q?levels=20setting=20not=20being=20respected?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + src/Plugin.php | 2 +- ..._fix_maxLevels_structured_productTypes.php | 42 +++++++++++++++++++ src/services/ProductTypes.php | 2 +- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 src/migrations/m241128_174712_fix_maxLevels_structured_productTypes.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f492d723f..0a27faa719 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Fixed a bug where a structured product type’s “Max Levels” setting wasn’t being respected. ([#3785](https://github.com/craftcms/commerce/issues/3785)) - Fixed a bug where users could access the Commerce user screen when the current user didn’t have permission. ## 5.2.6 - 2024-11-26 diff --git a/src/Plugin.php b/src/Plugin.php index e8764107b6..831b393d94 100755 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -257,7 +257,7 @@ public static function editions(): array /** * @inheritDoc */ - public string $schemaVersion = '5.2.0.5'; + public string $schemaVersion = '5.2.0.6'; /** * @inheritdoc diff --git a/src/migrations/m241128_174712_fix_maxLevels_structured_productTypes.php b/src/migrations/m241128_174712_fix_maxLevels_structured_productTypes.php new file mode 100644 index 0000000000..3483329e0f --- /dev/null +++ b/src/migrations/m241128_174712_fix_maxLevels_structured_productTypes.php @@ -0,0 +1,42 @@ +from('{{%commerce_producttypes}}') + ->where(['isStructure' => 1]) + ->andWhere(['not', ['maxLevels' => null]]) + ->collect(); + + // Loop through and update the `maxLevels` column in the `structures` table + $structuredProductTypesWithMaxLevels->each(function($productType) { + $this->update('{{%structures}}', ['maxLevels' => $productType['maxLevels']], ['id' => $productType['structureId']]); + }); + + return true; + } + + /** + * @inheritdoc + */ + public function safeDown(): bool + { + echo "m241128_174712_fix_maxLevels_structured_productTypes cannot be reverted.\n"; + return false; + } +} diff --git a/src/services/ProductTypes.php b/src/services/ProductTypes.php index ec5cd855da..d771561364 100755 --- a/src/services/ProductTypes.php +++ b/src/services/ProductTypes.php @@ -475,7 +475,7 @@ public function handleChangedProductType(ConfigEvent $event): void $structureUid = $data['structure']['uid']; $structure = Craft::$app->getStructures()->getStructureByUid($structureUid, true) ?? new Structure(['uid' => $structureUid]); $isNewStructure = empty($structure->id); - $structure->maxLevels = $data['structure']['maxLevels'] ?? null; + $structure->maxLevels = $data['maxLevels'] ?? null; Craft::$app->getStructures()->saveStructure($structure); $productTypeRecord->structureId = $structure->id; } else { From 5d0de8fc0d1b65c3dd5cc7b9769192bb31513251 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Thu, 28 Nov 2024 17:59:48 +0000 Subject: [PATCH 28/69] fix cs --- .../m241128_174712_fix_maxLevels_structured_productTypes.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/migrations/m241128_174712_fix_maxLevels_structured_productTypes.php b/src/migrations/m241128_174712_fix_maxLevels_structured_productTypes.php index 3483329e0f..4ead158992 100644 --- a/src/migrations/m241128_174712_fix_maxLevels_structured_productTypes.php +++ b/src/migrations/m241128_174712_fix_maxLevels_structured_productTypes.php @@ -2,7 +2,6 @@ namespace craft\commerce\migrations; -use Craft; use craft\db\Migration; use craft\db\Query; From b8ce3ba6f51f7374ebf53f5bd77c4bb8dd8dfe40 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Fri, 29 Nov 2024 15:10:06 +0000 Subject: [PATCH 29/69] Tweaks --- src/Plugin.php | 2 +- ...41128_174712_fix_maxLevels_structured_productTypes.php | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Plugin.php b/src/Plugin.php index 831b393d94..8fef95aa8d 100755 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -257,7 +257,7 @@ public static function editions(): array /** * @inheritDoc */ - public string $schemaVersion = '5.2.0.6'; + public string $schemaVersion = '5.2.7.0'; /** * @inheritdoc diff --git a/src/migrations/m241128_174712_fix_maxLevels_structured_productTypes.php b/src/migrations/m241128_174712_fix_maxLevels_structured_productTypes.php index 4ead158992..f07f69385d 100644 --- a/src/migrations/m241128_174712_fix_maxLevels_structured_productTypes.php +++ b/src/migrations/m241128_174712_fix_maxLevels_structured_productTypes.php @@ -2,8 +2,10 @@ namespace craft\commerce\migrations; +use craft\commerce\db\Table; use craft\db\Migration; use craft\db\Query; +use craft\db\Table as CraftTable; /** * m241128_174712_fix_maxLevels_structured_productTypes migration. @@ -17,14 +19,14 @@ public function safeUp(): bool { // Get all product types from Commerce project config $structuredProductTypesWithMaxLevels = (new Query()) - ->from('{{%commerce_producttypes}}') - ->where(['isStructure' => 1]) + ->from(Table::PRODUCTTYPES) + ->where(['isStructure' => true]) ->andWhere(['not', ['maxLevels' => null]]) ->collect(); // Loop through and update the `maxLevels` column in the `structures` table $structuredProductTypesWithMaxLevels->each(function($productType) { - $this->update('{{%structures}}', ['maxLevels' => $productType['maxLevels']], ['id' => $productType['structureId']]); + $this->update(CraftTable::STRUCTURES, ['maxLevels' => $productType['maxLevels']], ['id' => $productType['structureId']]); }); return true; From d6348fa6ec3a9ba67164736c50079580b2b6520a Mon Sep 17 00:00:00 2001 From: Brad Bell Date: Fri, 29 Nov 2024 11:15:25 -0800 Subject: [PATCH 30/69] php 8.2, 8.3, 8.4 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8666c06a79..307e4d0593 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: name: ci uses: craftcms/.github/.github/workflows/ci.yml@v3 with: - php_version: '8.2' + php_version: '["8.2", "8.3", "8.4"]' craft_version: '5' node_version: '20' jobs: '["ecs", "phpstan", "prettier", "tests"]' From 7d91e3b9c4fb095624d4c9cf03da186382c125ff Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Mon, 2 Dec 2024 11:56:17 +0000 Subject: [PATCH 31/69] bump deps --- composer.lock | 369 +++++++++++++++++++++++++------------------------- 1 file changed, 183 insertions(+), 186 deletions(-) diff --git a/composer.lock b/composer.lock index 44fc587fcd..1b85b101b2 100644 --- a/composer.lock +++ b/composer.lock @@ -331,16 +331,16 @@ }, { "name": "craftcms/cms", - "version": "5.5.0.1", + "version": "5.5.3", "source": { "type": "git", "url": "https://github.com/craftcms/cms.git", - "reference": "5e24a0bf74ea29ea2f5c04d4c9dcc325dcbb41ba" + "reference": "20199c5340825146f2354d0a29fb694b7d86e400" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/craftcms/cms/zipball/5e24a0bf74ea29ea2f5c04d4c9dcc325dcbb41ba", - "reference": "5e24a0bf74ea29ea2f5c04d4c9dcc325dcbb41ba", + "url": "https://api.github.com/repos/craftcms/cms/zipball/20199c5340825146f2354d0a29fb694b7d86e400", + "reference": "20199c5340825146f2354d0a29fb694b7d86e400", "shasum": "" }, "require": { @@ -454,7 +454,7 @@ "rss": "https://github.com/craftcms/cms/releases.atom", "source": "https://github.com/craftcms/cms" }, - "time": "2024-11-13T14:36:24+00:00" + "time": "2024-11-22T23:29:45+00:00" }, { "name": "craftcms/plugin-installer", @@ -1636,16 +1636,16 @@ }, { "name": "illuminate/collections", - "version": "v10.48.23", + "version": "v10.48.25", "source": { "type": "git", "url": "https://github.com/illuminate/collections.git", - "reference": "37c863cffb345869dd134eff8e646bc82a19cc96" + "reference": "48de3d6bc6aa779112ddcb608a3a96fc975d89d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/collections/zipball/37c863cffb345869dd134eff8e646bc82a19cc96", - "reference": "37c863cffb345869dd134eff8e646bc82a19cc96", + "url": "https://api.github.com/repos/illuminate/collections/zipball/48de3d6bc6aa779112ddcb608a3a96fc975d89d8", + "reference": "48de3d6bc6aa779112ddcb608a3a96fc975d89d8", "shasum": "" }, "require": { @@ -1687,20 +1687,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-06-19T14:25:05+00:00" + "time": "2024-11-21T14:02:44+00:00" }, { "name": "illuminate/conditionable", - "version": "v10.48.23", + "version": "v10.48.25", "source": { "type": "git", "url": "https://github.com/illuminate/conditionable.git", - "reference": "d0958e4741fc9d6f516a552060fd1b829a85e009" + "reference": "3ee34ac306fafc2a6f19cd7cd68c9af389e432a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/conditionable/zipball/d0958e4741fc9d6f516a552060fd1b829a85e009", - "reference": "d0958e4741fc9d6f516a552060fd1b829a85e009", + "url": "https://api.github.com/repos/illuminate/conditionable/zipball/3ee34ac306fafc2a6f19cd7cd68c9af389e432a5", + "reference": "3ee34ac306fafc2a6f19cd7cd68c9af389e432a5", "shasum": "" }, "require": { @@ -1733,20 +1733,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2023-02-03T08:06:17+00:00" + "time": "2024-11-21T14:02:44+00:00" }, { "name": "illuminate/contracts", - "version": "v10.48.23", + "version": "v10.48.25", "source": { "type": "git", "url": "https://github.com/illuminate/contracts.git", - "reference": "8d7152c4a1f5d9cf7da3e8b71f23e4556f6138ac" + "reference": "f90663a69f926105a70b78060a31f3c64e2d1c74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/contracts/zipball/8d7152c4a1f5d9cf7da3e8b71f23e4556f6138ac", - "reference": "8d7152c4a1f5d9cf7da3e8b71f23e4556f6138ac", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/f90663a69f926105a70b78060a31f3c64e2d1c74", + "reference": "f90663a69f926105a70b78060a31f3c64e2d1c74", "shasum": "" }, "require": { @@ -1781,11 +1781,11 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-01-15T18:52:32+00:00" + "time": "2024-11-21T14:02:44+00:00" }, { "name": "illuminate/macroable", - "version": "v10.48.23", + "version": "v10.48.25", "source": { "type": "git", "url": "https://github.com/illuminate/macroable.git", @@ -2182,23 +2182,23 @@ }, { "name": "moneyphp/money", - "version": "v4.5.1", + "version": "v4.6.0", "source": { "type": "git", "url": "https://github.com/moneyphp/money.git", - "reference": "142107bec4870ac2586057dc2fe917d25c92a91e" + "reference": "ddf6a86b574808f8844777ed4e8c4f92a10dac9b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/moneyphp/money/zipball/142107bec4870ac2586057dc2fe917d25c92a91e", - "reference": "142107bec4870ac2586057dc2fe917d25c92a91e", + "url": "https://api.github.com/repos/moneyphp/money/zipball/ddf6a86b574808f8844777ed4e8c4f92a10dac9b", + "reference": "ddf6a86b574808f8844777ed4e8c4f92a10dac9b", "shasum": "" }, "require": { "ext-bcmath": "*", "ext-filter": "*", "ext-json": "*", - "php": "~8.1.0 || ~8.2.0 || ~8.3.0" + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" }, "require-dev": { "cache/taggable-cache": "^1.1.0", @@ -2264,9 +2264,9 @@ ], "support": { "issues": "https://github.com/moneyphp/money/issues", - "source": "https://github.com/moneyphp/money/tree/v4.5.1" + "source": "https://github.com/moneyphp/money/tree/v4.6.0" }, - "time": "2024-09-27T12:04:27+00:00" + "time": "2024-11-22T10:59:03+00:00" }, { "name": "monolog/monolog", @@ -3954,16 +3954,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", "shasum": "" }, "require": { @@ -4001,7 +4001,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" }, "funding": [ { @@ -4017,20 +4017,20 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "87254c78dd50721cfd015b62277a8281c5589702" + "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/87254c78dd50721cfd015b62277a8281c5589702", - "reference": "87254c78dd50721cfd015b62277a8281c5589702", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/910c5db85a5356d0fea57680defec4e99eb9c8c1", + "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1", "shasum": "" }, "require": { @@ -4081,7 +4081,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.1.6" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.2.0" }, "funding": [ { @@ -4097,20 +4097,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50" + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/8f93aec25d41b72493c6ddff14e916177c9efc50", - "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7642f5e970b672283b7823222ae8ef8bbc160b9f", + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f", "shasum": "" }, "require": { @@ -4157,7 +4157,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.1" }, "funding": [ { @@ -4173,7 +4173,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/filesystem", @@ -4243,23 +4243,23 @@ }, { "name": "symfony/http-client", - "version": "v6.4.15", + "version": "v6.4.16", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "cb4073c905cd12b8496d24ac428a9228c1750670" + "reference": "60a113666fa67e598abace38e5f46a0954d8833d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/cb4073c905cd12b8496d24ac428a9228c1750670", - "reference": "cb4073c905cd12b8496d24ac428a9228c1750670", + "url": "https://api.github.com/repos/symfony/http-client/zipball/60a113666fa67e598abace38e5f46a0954d8833d", + "reference": "60a113666fa67e598abace38e5f46a0954d8833d", "shasum": "" }, "require": { "php": ">=8.1", "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-client-contracts": "^3.4.1", + "symfony/http-client-contracts": "~3.4.3|^3.5.1", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -4316,7 +4316,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v6.4.15" + "source": "https://github.com/symfony/http-client/tree/v6.4.16" }, "funding": [ { @@ -4332,20 +4332,20 @@ "type": "tidelift" } ], - "time": "2024-11-13T13:40:18+00:00" + "time": "2024-11-27T11:52:33+00:00" }, { "name": "symfony/http-client-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "20414d96f391677bf80078aa55baece78b82647d" + "reference": "c2f3ad828596624ca39ea40f83617ef51ca8bbf9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/20414d96f391677bf80078aa55baece78b82647d", - "reference": "20414d96f391677bf80078aa55baece78b82647d", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/c2f3ad828596624ca39ea40f83617ef51ca8bbf9", + "reference": "c2f3ad828596624ca39ea40f83617ef51ca8bbf9", "shasum": "" }, "require": { @@ -4394,7 +4394,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.1" }, "funding": [ { @@ -4410,20 +4410,20 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-11-25T12:02:18+00:00" }, { "name": "symfony/mailer", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "69c9948451fb3a6a4d47dc8261d1794734e76cdd" + "reference": "e4d358702fb66e4c8a2af08e90e7271a62de39cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/69c9948451fb3a6a4d47dc8261d1794734e76cdd", - "reference": "69c9948451fb3a6a4d47dc8261d1794734e76cdd", + "url": "https://api.github.com/repos/symfony/mailer/zipball/e4d358702fb66e4c8a2af08e90e7271a62de39cc", + "reference": "e4d358702fb66e4c8a2af08e90e7271a62de39cc", "shasum": "" }, "require": { @@ -4432,7 +4432,7 @@ "psr/event-dispatcher": "^1", "psr/log": "^1|^2|^3", "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", + "symfony/mime": "^7.2", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -4474,7 +4474,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.1.6" + "source": "https://github.com/symfony/mailer/tree/v7.2.0" }, "funding": [ { @@ -4490,20 +4490,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-11-25T15:21:05+00:00" }, { "name": "symfony/mime", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "caa1e521edb2650b8470918dfe51708c237f0598" + "reference": "cc84a4b81f62158c3846ac7ff10f696aae2b524d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/caa1e521edb2650b8470918dfe51708c237f0598", - "reference": "caa1e521edb2650b8470918dfe51708c237f0598", + "url": "https://api.github.com/repos/symfony/mime/zipball/cc84a4b81f62158c3846ac7ff10f696aae2b524d", + "reference": "cc84a4b81f62158c3846ac7ff10f696aae2b524d", "shasum": "" }, "require": { @@ -4558,7 +4558,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.1.6" + "source": "https://github.com/symfony/mime/tree/v7.2.0" }, "funding": [ { @@ -4574,7 +4574,7 @@ "type": "tidelift" } ], - "time": "2024-10-25T15:11:02+00:00" + "time": "2024-11-23T09:19:39+00:00" }, { "name": "symfony/polyfill-ctype", @@ -5279,16 +5279,16 @@ }, { "name": "symfony/process", - "version": "v7.1.8", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "42783370fda6e538771f7c7a36e9fa2ee3a84892" + "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/42783370fda6e538771f7c7a36e9fa2ee3a84892", - "reference": "42783370fda6e538771f7c7a36e9fa2ee3a84892", + "url": "https://api.github.com/repos/symfony/process/zipball/d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", + "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", "shasum": "" }, "require": { @@ -5320,7 +5320,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.1.8" + "source": "https://github.com/symfony/process/tree/v7.2.0" }, "funding": [ { @@ -5336,20 +5336,20 @@ "type": "tidelift" } ], - "time": "2024-11-06T14:23:19+00:00" + "time": "2024-11-06T14:24:19+00:00" }, { "name": "symfony/property-access", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/property-access.git", - "reference": "975d7f7fd8fcb952364c6badc46d01a580532bf9" + "reference": "3ae42efba01e45aaedecf5c93c8d6a3ab3a82276" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/975d7f7fd8fcb952364c6badc46d01a580532bf9", - "reference": "975d7f7fd8fcb952364c6badc46d01a580532bf9", + "url": "https://api.github.com/repos/symfony/property-access/zipball/3ae42efba01e45aaedecf5c93c8d6a3ab3a82276", + "reference": "3ae42efba01e45aaedecf5c93c8d6a3ab3a82276", "shasum": "" }, "require": { @@ -5396,7 +5396,7 @@ "reflection" ], "support": { - "source": "https://github.com/symfony/property-access/tree/v7.1.6" + "source": "https://github.com/symfony/property-access/tree/v7.2.0" }, "funding": [ { @@ -5412,20 +5412,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-26T12:28:35+00:00" }, { "name": "symfony/property-info", - "version": "v7.1.8", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/property-info.git", - "reference": "3748f85f64351d282fd028e44309856f1d79142e" + "reference": "b00580d9d7c9654e1df95df85105d0da67418b3f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/3748f85f64351d282fd028e44309856f1d79142e", - "reference": "3748f85f64351d282fd028e44309856f1d79142e", + "url": "https://api.github.com/repos/symfony/property-info/zipball/b00580d9d7c9654e1df95df85105d0da67418b3f", + "reference": "b00580d9d7c9654e1df95df85105d0da67418b3f", "shasum": "" }, "require": { @@ -5436,8 +5436,7 @@ "conflict": { "phpdocumentor/reflection-docblock": "<5.2", "phpdocumentor/type-resolver": "<1.5.1", - "symfony/dependency-injection": "<6.4", - "symfony/serializer": "<6.4" + "symfony/dependency-injection": "<6.4" }, "require-dev": { "phpdocumentor/reflection-docblock": "^5.2", @@ -5480,7 +5479,7 @@ "validator" ], "support": { - "source": "https://github.com/symfony/property-info/tree/v7.1.8" + "source": "https://github.com/symfony/property-info/tree/v7.2.0" }, "funding": [ { @@ -5496,7 +5495,7 @@ "type": "tidelift" } ], - "time": "2024-11-09T07:07:11+00:00" + "time": "2024-11-27T09:50:52+00:00" }, { "name": "symfony/serializer", @@ -5598,16 +5597,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", "shasum": "" }, "require": { @@ -5661,7 +5660,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" }, "funding": [ { @@ -5677,20 +5676,20 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/string", - "version": "v7.1.8", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "591ebd41565f356fcd8b090fe64dbb5878f50281" + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/591ebd41565f356fcd8b090fe64dbb5878f50281", - "reference": "591ebd41565f356fcd8b090fe64dbb5878f50281", + "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82", + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82", "shasum": "" }, "require": { @@ -5748,7 +5747,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.8" + "source": "https://github.com/symfony/string/tree/v7.2.0" }, "funding": [ { @@ -5764,20 +5763,20 @@ "type": "tidelift" } ], - "time": "2024-11-13T13:31:21+00:00" + "time": "2024-11-13T13:31:26+00:00" }, { "name": "symfony/type-info", - "version": "v7.1.8", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/type-info.git", - "reference": "51535dde21c7abf65c9d000a30bb15f6478195e6" + "reference": "e0bfd95bceb3886c59487828537691aecb7d9c6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/type-info/zipball/51535dde21c7abf65c9d000a30bb15f6478195e6", - "reference": "51535dde21c7abf65c9d000a30bb15f6478195e6", + "url": "https://api.github.com/repos/symfony/type-info/zipball/e0bfd95bceb3886c59487828537691aecb7d9c6b", + "reference": "e0bfd95bceb3886c59487828537691aecb7d9c6b", "shasum": "" }, "require": { @@ -5786,13 +5785,11 @@ }, "conflict": { "phpstan/phpdoc-parser": "<1.0", - "symfony/dependency-injection": "<6.4", - "symfony/property-info": "<6.4" + "symfony/dependency-injection": "<6.4" }, "require-dev": { "phpstan/phpdoc-parser": "^1.0|^2.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/property-info": "^6.4|^7.0" + "symfony/dependency-injection": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -5830,7 +5827,7 @@ "type" ], "support": { - "source": "https://github.com/symfony/type-info/tree/v7.1.8" + "source": "https://github.com/symfony/type-info/tree/v7.2.0" }, "funding": [ { @@ -5846,20 +5843,20 @@ "type": "tidelift" } ], - "time": "2024-11-07T15:49:33+00:00" + "time": "2024-11-18T09:51:31+00:00" }, { "name": "symfony/uid", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "65befb3bb2d503bbffbd08c815aa38b472999917" + "reference": "2d294d0c48df244c71c105a169d0190bfb080426" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/65befb3bb2d503bbffbd08c815aa38b472999917", - "reference": "65befb3bb2d503bbffbd08c815aa38b472999917", + "url": "https://api.github.com/repos/symfony/uid/zipball/2d294d0c48df244c71c105a169d0190bfb080426", + "reference": "2d294d0c48df244c71c105a169d0190bfb080426", "shasum": "" }, "require": { @@ -5904,7 +5901,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.1.6" + "source": "https://github.com/symfony/uid/tree/v7.2.0" }, "funding": [ { @@ -5920,7 +5917,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/var-dumper", @@ -6525,16 +6522,16 @@ }, { "name": "voku/portable-ascii", - "version": "2.0.1", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/voku/portable-ascii.git", - "reference": "b56450eed252f6801410d810c8e1727224ae0743" + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743", - "reference": "b56450eed252f6801410d810c8e1727224ae0743", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", "shasum": "" }, "require": { @@ -6559,7 +6556,7 @@ "authors": [ { "name": "Lars Moelleken", - "homepage": "http://www.moelleken.org/" + "homepage": "https://www.moelleken.org/" } ], "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", @@ -6571,7 +6568,7 @@ ], "support": { "issues": "https://github.com/voku/portable-ascii/issues", - "source": "https://github.com/voku/portable-ascii/tree/2.0.1" + "source": "https://github.com/voku/portable-ascii/tree/2.0.3" }, "funding": [ { @@ -6595,7 +6592,7 @@ "type": "tidelift" } ], - "time": "2022-03-08T17:03:00+00:00" + "time": "2024-11-21T01:49:47+00:00" }, { "name": "voku/portable-utf8", @@ -8711,16 +8708,16 @@ }, { "name": "fakerphp/faker", - "version": "v1.24.0", + "version": "v1.24.1", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "a136842a532bac9ecd8a1c723852b09915d7db50" + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/a136842a532bac9ecd8a1c723852b09915d7db50", - "reference": "a136842a532bac9ecd8a1c723852b09915d7db50", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", "shasum": "" }, "require": { @@ -8768,9 +8765,9 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.24.0" + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" }, - "time": "2024-11-07T15:11:20+00:00" + "time": "2024-11-21T13:46:39+00:00" }, { "name": "graham-campbell/result-type", @@ -9423,16 +9420,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.10", + "version": "1.12.12", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "fc463b5d0fe906dcf19689be692c65c50406a071" + "reference": "b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/fc463b5d0fe906dcf19689be692c65c50406a071", - "reference": "fc463b5d0fe906dcf19689be692c65c50406a071", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0", + "reference": "b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0", "shasum": "" }, "require": { @@ -9477,7 +9474,7 @@ "type": "github" } ], - "time": "2024-11-11T15:37:09+00:00" + "time": "2024-11-28T22:13:23+00:00" }, { "name": "phpunit/php-code-coverage", @@ -9804,16 +9801,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.4.3", + "version": "11.4.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "e8e8ed1854de5d36c088ec1833beae40d2dedd76" + "reference": "f9ba7bd3c9f3ff54ec379d7a1c2e3f13fe0bbde4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e8e8ed1854de5d36c088ec1833beae40d2dedd76", - "reference": "e8e8ed1854de5d36c088ec1833beae40d2dedd76", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f9ba7bd3c9f3ff54ec379d7a1c2e3f13fe0bbde4", + "reference": "f9ba7bd3c9f3ff54ec379d7a1c2e3f13fe0bbde4", "shasum": "" }, "require": { @@ -9823,7 +9820,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.0", + "myclabs/deep-copy": "^1.12.1", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.2", @@ -9834,7 +9831,7 @@ "phpunit/php-timer": "^7.0.1", "sebastian/cli-parser": "^3.0.2", "sebastian/code-unit": "^3.0.1", - "sebastian/comparator": "^6.1.1", + "sebastian/comparator": "^6.2.1", "sebastian/diff": "^6.0.2", "sebastian/environment": "^7.2.0", "sebastian/exporter": "^6.1.3", @@ -9884,7 +9881,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.4.3" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.4.4" }, "funding": [ { @@ -9900,20 +9897,20 @@ "type": "tidelift" } ], - "time": "2024-10-28T13:07:50+00:00" + "time": "2024-11-27T10:44:52+00:00" }, { "name": "psy/psysh", - "version": "v0.12.4", + "version": "v0.12.5", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "2fd717afa05341b4f8152547f142cd2f130f6818" + "reference": "36a03ff27986682c22985e56aabaf840dd173cb5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/2fd717afa05341b4f8152547f142cd2f130f6818", - "reference": "2fd717afa05341b4f8152547f142cd2f130f6818", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/36a03ff27986682c22985e56aabaf840dd173cb5", + "reference": "36a03ff27986682c22985e56aabaf840dd173cb5", "shasum": "" }, "require": { @@ -9940,12 +9937,12 @@ ], "type": "library", "extra": { - "branch-alias": { - "dev-main": "0.12.x-dev" - }, "bamarni-bin": { "bin-links": false, "forward-command": false + }, + "branch-alias": { + "dev-main": "0.12.x-dev" } }, "autoload": { @@ -9977,9 +9974,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.4" + "source": "https://github.com/bobthecow/psysh/tree/v0.12.5" }, - "time": "2024-06-10T01:18:23+00:00" + "time": "2024-11-29T06:14:30+00:00" }, { "name": "rector/rector", @@ -11034,16 +11031,16 @@ }, { "name": "symfony/browser-kit", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "714becc9ba9b20115ffededc58f6b7172dc394cf" + "reference": "8d64d17e198082f8f198d023a6b634e7b5fdda94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/714becc9ba9b20115ffededc58f6b7172dc394cf", - "reference": "714becc9ba9b20115ffededc58f6b7172dc394cf", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/8d64d17e198082f8f198d023a6b634e7b5fdda94", + "reference": "8d64d17e198082f8f198d023a6b634e7b5fdda94", "shasum": "" }, "require": { @@ -11082,7 +11079,7 @@ "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/browser-kit/tree/v7.1.6" + "source": "https://github.com/symfony/browser-kit/tree/v7.2.0" }, "funding": [ { @@ -11098,20 +11095,20 @@ "type": "tidelift" } ], - "time": "2024-10-25T15:11:02+00:00" + "time": "2024-10-25T15:15:23+00:00" }, { "name": "symfony/console", - "version": "v7.1.8", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "ff04e5b5ba043d2badfb308197b9e6b42883fcd5" + "reference": "23c8aae6d764e2bae02d2a99f7532a7f6ed619cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/ff04e5b5ba043d2badfb308197b9e6b42883fcd5", - "reference": "ff04e5b5ba043d2badfb308197b9e6b42883fcd5", + "url": "https://api.github.com/repos/symfony/console/zipball/23c8aae6d764e2bae02d2a99f7532a7f6ed619cf", + "reference": "23c8aae6d764e2bae02d2a99f7532a7f6ed619cf", "shasum": "" }, "require": { @@ -11175,7 +11172,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.8" + "source": "https://github.com/symfony/console/tree/v7.2.0" }, "funding": [ { @@ -11191,20 +11188,20 @@ "type": "tidelift" } ], - "time": "2024-11-06T14:23:19+00:00" + "time": "2024-11-06T14:24:19+00:00" }, { "name": "symfony/css-selector", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "4aa4f6b3d6749c14d3aa815eef8226632e7bbc66" + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/4aa4f6b3d6749c14d3aa815eef8226632e7bbc66", - "reference": "4aa4f6b3d6749c14d3aa815eef8226632e7bbc66", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2", "shasum": "" }, "require": { @@ -11240,7 +11237,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v7.1.6" + "source": "https://github.com/symfony/css-selector/tree/v7.2.0" }, "funding": [ { @@ -11256,20 +11253,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/dom-crawler", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "794ddd5481ba15d8a04132c95e211cd5656e09fb" + "reference": "b176e1f1f550ef44c94eb971bf92488de08f7c6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/794ddd5481ba15d8a04132c95e211cd5656e09fb", - "reference": "794ddd5481ba15d8a04132c95e211cd5656e09fb", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/b176e1f1f550ef44c94eb971bf92488de08f7c6b", + "reference": "b176e1f1f550ef44c94eb971bf92488de08f7c6b", "shasum": "" }, "require": { @@ -11307,7 +11304,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v7.1.6" + "source": "https://github.com/symfony/dom-crawler/tree/v7.2.0" }, "funding": [ { @@ -11323,20 +11320,20 @@ "type": "tidelift" } ], - "time": "2024-10-25T15:11:02+00:00" + "time": "2024-11-13T16:15:23+00:00" }, { "name": "symfony/finder", - "version": "v7.1.6", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8" + "reference": "6de263e5868b9a137602dd1e33e4d48bfae99c49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/2cb89664897be33f78c65d3d2845954c8d7a43b8", - "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8", + "url": "https://api.github.com/repos/symfony/finder/zipball/6de263e5868b9a137602dd1e33e4d48bfae99c49", + "reference": "6de263e5868b9a137602dd1e33e4d48bfae99c49", "shasum": "" }, "require": { @@ -11371,7 +11368,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.1.6" + "source": "https://github.com/symfony/finder/tree/v7.2.0" }, "funding": [ { @@ -11387,7 +11384,7 @@ "type": "tidelift" } ], - "time": "2024-10-01T08:31:23+00:00" + "time": "2024-10-23T06:56:12+00:00" }, { "name": "symfony/polyfill-php80", @@ -11671,6 +11668,6 @@ "platform": { "php": "^8.2" }, - "platform-dev": [], + "platform-dev": {}, "plugin-api-version": "2.6.0" } From 19e466e2c41493b14138eadbb6d7777c26fb4424 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Mon, 2 Dec 2024 12:14:18 +0000 Subject: [PATCH 32/69] No `8.4` yet https://github.com/craftcms/.github/commit/74c8aa46e9d9ab463cc1ed0a71585477b11dcdcd [ci skip] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 307e4d0593..f1338b9a42 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: name: ci uses: craftcms/.github/.github/workflows/ci.yml@v3 with: - php_version: '["8.2", "8.3", "8.4"]' + php_version: '["8.2", "8.3"]' craft_version: '5' node_version: '20' jobs: '["ecs", "phpstan", "prettier", "tests"]' From 22899b925688fba5c6898d4f227e230b3850493e Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Mon, 2 Dec 2024 22:42:30 +0800 Subject: [PATCH 33/69] added todo --- src/services/Plans.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/services/Plans.php b/src/services/Plans.php index 472d3a4caf..31611c5dad 100644 --- a/src/services/Plans.php +++ b/src/services/Plans.php @@ -129,6 +129,7 @@ public function getPlansByGatewayId(int $gatewayId): array * * @return Plan[] * @deprecated in 4.0. Use [[getPlansByGatewayId]] instead. + * TODO: remove in 6.0 */ public function getAllGatewayPlans(int $gatewayId): array { From 7aac5358ded27963a830514c242973093099b856 Mon Sep 17 00:00:00 2001 From: brandonkelly Date: Mon, 2 Dec 2024 13:58:06 -0800 Subject: [PATCH 34/69] not necessary --- src/templates/orders/_index.twig | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/templates/orders/_index.twig b/src/templates/orders/_index.twig index 166cadf630..a254291ff8 100644 --- a/src/templates/orders/_index.twig +++ b/src/templates/orders/_index.twig @@ -18,16 +18,6 @@ "Total Revenue", ]) %} -{% block initJs %} - Craft.elementIndex = Craft.createElementIndex('{{ elementType|e("js") }}', $('#page-container'), { - elementTypeName: '{{ elementInstance.displayName()|e("js") }}', - elementTypePluralName: '{{ elementInstance.pluralDisplayName()|e("js") }}', - context: '{{ context }}', - storageKey: 'elementindex.{{ elementType|e("js") }}', - toolbarSelector: '#toolbar' - }); -{% endblock %} - {% if orderStatusHandle is defined and orderStatusHandle %} {% js %} window.defaultStatusHandle = '{{ orderStatusHandle }}'; From 22b8b34a5e7187c3688a7975ff1e1fd35e70c2a8 Mon Sep 17 00:00:00 2001 From: brandonkelly Date: Mon, 2 Dec 2024 13:59:15 -0800 Subject: [PATCH 35/69] Fixed #3793 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2889e0be6..242c72f983 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Release Notes for Craft Commerce +## Unreleased + +- Fixed an error that occurred on the Orders index page. ([#3793](https://github.com/craftcms/commerce/issues/3793)) + ## 4.7.0 - 2024-10-16 ### Store Management From c5c714a7cfd34a3a6456d31a62575f1efb4f9722 Mon Sep 17 00:00:00 2001 From: brandonkelly Date: Mon, 2 Dec 2024 14:07:24 -0800 Subject: [PATCH 36/69] Finish 4.7.1 --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 242c72f983..7b86159db3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ # Release Notes for Craft Commerce -## Unreleased +## 4.7.1 - 2024-12-02 -- Fixed an error that occurred on the Orders index page. ([#3793](https://github.com/craftcms/commerce/issues/3793)) +- Fixed an error that occurred on the Orders index page when running Craft CMS 4.13.4 or later. ([#3793](https://github.com/craftcms/commerce/issues/3793)) ## 4.7.0 - 2024-10-16 From 0dded042cf25e4e5dd56a303a1512722c0642120 Mon Sep 17 00:00:00 2001 From: brandonkelly Date: Mon, 2 Dec 2024 14:09:42 -0800 Subject: [PATCH 37/69] Finish 5.2.7 --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73f4df3d54..089c31dd94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,10 @@ # Release Notes for Craft Commerce -## Unreleased +## 5.2.7 - 2024-11 - Fixed an error that occurred on the Orders index page when running Craft CMS 5.5.4 or later. ([#3793](https://github.com/craftcms/commerce/issues/3793)) - Fixed a bug where a structured product type’s “Max Levels” setting wasn’t being respected. ([#3785](https://github.com/craftcms/commerce/issues/3785)) -- Fixed a bug where users could access the Commerce user screen when the current user didn’t have permission. +- Fixed an information disclosure vulnerability. ## 5.2.6 - 2024-11-26 From c0f47950c0ec49b16efe1594e24f8f747a908017 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Tue, 3 Dec 2024 10:17:58 +0000 Subject: [PATCH 38/69] #3792 Purchasbale line items missing their CP edit URL on order edit --- CHANGELOG.md | 4 ++++ src/elements/Variant.php | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 089c31dd94..364bd28bab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Release Notes for Craft Commerce +## Unreleased + +- Fixed a bug where purchasable line items were missing their CP edit URL on the Edit Order page. ([#3792](https://github.com/craftcms/commerce/issues/3792)) + ## 5.2.7 - 2024-11 - Fixed an error that occurred on the Orders index page when running Craft CMS 5.5.4 or later. ([#3793](https://github.com/craftcms/commerce/issues/3793)) diff --git a/src/elements/Variant.php b/src/elements/Variant.php index f0ddc22b28..b052f0cf83 100755 --- a/src/elements/Variant.php +++ b/src/elements/Variant.php @@ -740,6 +740,14 @@ public function canView(User $user): bool return $product->canView($user); } + /** + * @inheritdoc + */ + public function getCpEditUrl(): ?string + { + return $this->getOwner() ? $this->getOwner()->getCpEditUrl() : ''; + } + /** * @inheritdoc */ From 4c12360d9cf84f6c5a29ff5935287a7ddfdc292d Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Wed, 4 Dec 2024 15:53:06 +0800 Subject: [PATCH 39/69] include locale value normalization Fixes $3789 --- CHANGELOG.md | 4 ++++ src/controllers/OrdersController.php | 4 ++++ .../_components/gateways/_modalWrapper.twig | 14 +++++++++++--- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 089c31dd94..b6d67c23cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Release Notes for Craft Commerce +## Unreleased + +- Fixed a bug where the price was not formatted correctly according to the locale in the payment model on the Order Edit screens. ([#3789](https://github.com/craftcms/commerce/issues/3789)) + ## 5.2.7 - 2024-11 - Fixed an error that occurred on the Orders index page when running Craft CMS 5.5.4 or later. ([#3793](https://github.com/craftcms/commerce/issues/3793)) diff --git a/src/controllers/OrdersController.php b/src/controllers/OrdersController.php index 87f871c06a..9b4d07f076 100644 --- a/src/controllers/OrdersController.php +++ b/src/controllers/OrdersController.php @@ -1236,11 +1236,15 @@ public function actionPaymentAmountData(): Response $paymentCurrencies = Plugin::getInstance()->getPaymentCurrencies(); $paymentCurrency = $this->request->getRequiredParam('paymentCurrency'); $paymentAmount = $this->request->getRequiredParam('paymentAmount'); + $locale = $this->request->getRequiredParam('locale'); $orderId = $this->request->getRequiredParam('orderId'); /** @var Order $order */ $order = Order::find()->id($orderId)->one(); $baseCurrency = $order->currency; + $paymentAmount = MoneyHelper::toMoney(['value' => $paymentAmount, 'currency' => $baseCurrency, 'locale' => $locale]); + $paymentAmount = MoneyHelper::toDecimal($paymentAmount); + $baseCurrencyPaymentAmount = $paymentCurrencies->convertCurrency($paymentAmount, $paymentCurrency, $baseCurrency); $baseCurrencyPaymentAmountAsCurrency = Craft::t('commerce', 'Pay {amount} of {currency} on the order.', ['amount' => Currency::formatAsCurrency($baseCurrencyPaymentAmount, $baseCurrency), 'currency' => $baseCurrency]); diff --git a/src/templates/_components/gateways/_modalWrapper.twig b/src/templates/_components/gateways/_modalWrapper.twig index 06ab826d5a..948ff011b4 100644 --- a/src/templates/_components/gateways/_modalWrapper.twig +++ b/src/templates/_components/gateways/_modalWrapper.twig @@ -20,9 +20,15 @@ {{ formHtml|raw }}
- Payment Amount + {{ "Payment Amount"|t('commerce') }}
- + + {% set currencies = craft.commerce.paymentCurrencies.getAllPaymentCurrencies() %} {% set primaryCurrency = craft.commerce.paymentCurrencies.getPrimaryPaymentCurrency() %} @@ -77,7 +83,8 @@ function updatePrice(form) { - var price = form.find("input.paymentAmount").val(); + var price = form.find("input[name='paymentAmount[value]']").val(); + var locale = form.find("input[name='paymentAmount[locale]']").val(); $.ajax({ type: "POST", @@ -89,6 +96,7 @@ data: { 'action' : 'commerce/orders/payment-amount-data', 'paymentAmount': price, + 'locale': locale, 'paymentCurrency': form.find(".paymentCurrency").val(), 'orderId' : orderId }, From eba27fffd0109ad517adc759e63d2f8c7d9413de Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Wed, 4 Dec 2024 08:03:11 +0000 Subject: [PATCH 40/69] tidy --- src/elements/Variant.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/elements/Variant.php b/src/elements/Variant.php index b052f0cf83..1ab9f48363 100755 --- a/src/elements/Variant.php +++ b/src/elements/Variant.php @@ -745,7 +745,7 @@ public function canView(User $user): bool */ public function getCpEditUrl(): ?string { - return $this->getOwner() ? $this->getOwner()->getCpEditUrl() : ''; + return $this->getOwner() ? $this->getOwner()->getCpEditUrl() : null; } /** From 6eb58c492b0b525063e619f4c359678566aef5a6 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Wed, 4 Dec 2024 10:10:51 +0000 Subject: [PATCH 41/69] #3786 correctly save environment variables on store settings --- CHANGELOG.md | 1 + src/Plugin.php | 2 +- src/migrations/Install.php | 22 ++--- ...091901_fix_store_environment_variables.php | 97 +++++++++++++++++++ 4 files changed, 110 insertions(+), 12 deletions(-) create mode 100644 src/migrations/m241204_091901_fix_store_environment_variables.php diff --git a/CHANGELOG.md b/CHANGELOG.md index b6d67c23cc..12e9e9f024 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased - Fixed a bug where the price was not formatted correctly according to the locale in the payment model on the Order Edit screens. ([#3789](https://github.com/craftcms/commerce/issues/3789)) +- Fixed a bug where it wasn’t possible to set store settings to environment variables. ([#3786](https://github.com/craftcms/commerce/issues/3786)) ## 5.2.7 - 2024-11 diff --git a/src/Plugin.php b/src/Plugin.php index 8fef95aa8d..201e9b0bdb 100755 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -257,7 +257,7 @@ public static function editions(): array /** * @inheritDoc */ - public string $schemaVersion = '5.2.7.0'; + public string $schemaVersion = '5.2.7.1'; /** * @inheritdoc diff --git a/src/migrations/Install.php b/src/migrations/Install.php index defc1bbf0c..756520a8ec 100644 --- a/src/migrations/Install.php +++ b/src/migrations/Install.php @@ -857,17 +857,17 @@ public function createTables(): void 'handle' => $this->string()->notNull(), 'primary' => $this->boolean()->notNull(), 'currency' => $this->string()->notNull()->defaultValue('USD'), - 'autoSetCartShippingMethodOption' => $this->boolean()->notNull()->defaultValue(false), - 'autoSetNewCartAddresses' => $this->boolean()->notNull()->defaultValue(false), - 'autoSetPaymentSource' => $this->boolean()->notNull()->defaultValue(false), - 'allowEmptyCartOnCheckout' => $this->boolean()->notNull()->defaultValue(false), - 'allowCheckoutWithoutPayment' => $this->boolean()->notNull()->defaultValue(false), - 'allowPartialPaymentOnCheckout' => $this->boolean()->notNull()->defaultValue(false), - 'requireShippingAddressAtCheckout' => $this->boolean()->notNull()->defaultValue(false), - 'requireBillingAddressAtCheckout' => $this->boolean()->notNull()->defaultValue(false), - 'requireShippingMethodSelectionAtCheckout' => $this->boolean()->notNull()->defaultValue(false), - 'useBillingAddressForTax' => $this->boolean()->notNull()->defaultValue(false), - 'validateOrganizationTaxIdAsVatId' => $this->boolean()->notNull()->defaultValue(false), + 'autoSetCartShippingMethodOption' => $this->string()->notNull()->defaultValue('false'), + 'autoSetNewCartAddresses' => $this->string()->notNull()->defaultValue('false'), + 'autoSetPaymentSource' => $this->string()->notNull()->defaultValue('false'), + 'allowEmptyCartOnCheckout' => $this->string()->notNull()->defaultValue('false'), + 'allowCheckoutWithoutPayment' => $this->string()->notNull()->defaultValue('false'), + 'allowPartialPaymentOnCheckout' => $this->string()->notNull()->defaultValue('false'), + 'requireShippingAddressAtCheckout' => $this->string()->notNull()->defaultValue('false'), + 'requireBillingAddressAtCheckout' => $this->string()->notNull()->defaultValue('false'), + 'requireShippingMethodSelectionAtCheckout' => $this->string()->notNull()->defaultValue('false'), + 'useBillingAddressForTax' => $this->string()->notNull()->defaultValue('false'), + 'validateOrganizationTaxIdAsVatId' => $this->string()->notNull()->defaultValue('false'), 'orderReferenceFormat' => $this->string(), 'freeOrderPaymentStrategy' => $this->string()->defaultValue('complete'), 'minimumTotalPriceStrategy' => $this->string()->defaultValue('default'), diff --git a/src/migrations/m241204_091901_fix_store_environment_variables.php b/src/migrations/m241204_091901_fix_store_environment_variables.php new file mode 100644 index 0000000000..4b11171509 --- /dev/null +++ b/src/migrations/m241204_091901_fix_store_environment_variables.php @@ -0,0 +1,97 @@ +from('{{%commerce_stores}}') + ->all(); + + // Get the store settings for each store from the project config + $storeSettings = \Craft::$app->getProjectConfig()->get('commerce.stores'); + + + // Store properties to update + $storeProperties = [ + 'autoSetNewCartAddresses', + 'autoSetCartShippingMethodOption', + 'autoSetPaymentSource', + 'allowEmptyCartOnCheckout', + 'allowCheckoutWithoutPayment', + 'allowPartialPaymentOnCheckout', + 'requireShippingAddressAtCheckout', + 'requireBillingAddressAtCheckout', + 'requireShippingMethodSelectionAtCheckout', + 'useBillingAddressForTax', + 'validateOrganizationTaxIdAsVatId', + ]; + + // Update stores env var DB columns + foreach ($storeProperties as $storeProperty) { + $this->alterColumn('{{%commerce_stores}}', $storeProperty, $this->string()->notNull()->defaultValue('false')); + } + + // Loop through each store and update values in the DB to match the PC values + foreach ($stores as $store) { + $storeSettingsForStore = $storeSettings[$store['uid']] ?? null; + + // If there isn't data in the PC for this store, skip it + if (!$storeSettingsForStore) { + continue; + } + + $updateData = []; + foreach ($storeProperties as $storeProperty) { + // If there isn't data in the PC for this store property, skip it + if (!isset($storeSettingsForStore[$storeProperty])) { + continue; + } + + // If the value in PC is a bool and the same as the DB value, skip it + if (in_array($storeSettingsForStore[$storeProperty], ['0', '1', 0, 1, false, true, 'false', 'true'], true) && $storeSettingsForStore[$storeProperty] == $store[$storeProperty]) { + continue; + } + + // If the value in PC is a string and is different from the DB value, skip it to avoid change in behavior + $envVarValue = App::parseBooleanEnv($storeSettingsForStore[$storeProperty]); + if ($envVarValue != $store[$storeProperty]) { + continue; + } + + // Else update the DB with the environment variable name + $updateData[$storeProperty] = $storeSettingsForStore[$storeProperty]; + } + + if (empty($updateData)) { + continue; + } + + $this->update('{{%commerce_stores}}', $updateData, ['id' => $store['id']]); + } + + return true; + } + + /** + * @inheritdoc + */ + public function safeDown(): bool + { + echo "m241204_091901_fix_store_environment_variables cannot be reverted.\n"; + return false; + } +} From 251b19ae8329f04111c0ae5ce8b2945e996286a8 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Wed, 4 Dec 2024 10:13:02 +0000 Subject: [PATCH 42/69] Use table constants --- .../m241204_091901_fix_store_environment_variables.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/migrations/m241204_091901_fix_store_environment_variables.php b/src/migrations/m241204_091901_fix_store_environment_variables.php index 4b11171509..ea0d504579 100644 --- a/src/migrations/m241204_091901_fix_store_environment_variables.php +++ b/src/migrations/m241204_091901_fix_store_environment_variables.php @@ -2,6 +2,7 @@ namespace craft\commerce\migrations; +use craft\commerce\db\Table; use craft\db\Migration; use craft\db\Query; use craft\helpers\App; @@ -18,7 +19,7 @@ public function safeUp(): bool { // Get all the stores current data $stores = (new Query()) - ->from('{{%commerce_stores}}') + ->from(Table::STORES) ->all(); // Get the store settings for each store from the project config @@ -42,7 +43,7 @@ public function safeUp(): bool // Update stores env var DB columns foreach ($storeProperties as $storeProperty) { - $this->alterColumn('{{%commerce_stores}}', $storeProperty, $this->string()->notNull()->defaultValue('false')); + $this->alterColumn(Table::STORES, $storeProperty, $this->string()->notNull()->defaultValue('false')); } // Loop through each store and update values in the DB to match the PC values @@ -80,7 +81,7 @@ public function safeUp(): bool continue; } - $this->update('{{%commerce_stores}}', $updateData, ['id' => $store['id']]); + $this->update(Table::STORES, $updateData, ['id' => $store['id']]); } return true; From 8ed09c9c2e5820cdcb719656cb3cb85f73214cde Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Wed, 4 Dec 2024 10:17:44 +0000 Subject: [PATCH 43/69] PHPStan fix --- src/controllers/OrdersController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/OrdersController.php b/src/controllers/OrdersController.php index 9b4d07f076..fbeac219fa 100644 --- a/src/controllers/OrdersController.php +++ b/src/controllers/OrdersController.php @@ -1245,7 +1245,7 @@ public function actionPaymentAmountData(): Response $paymentAmount = MoneyHelper::toMoney(['value' => $paymentAmount, 'currency' => $baseCurrency, 'locale' => $locale]); $paymentAmount = MoneyHelper::toDecimal($paymentAmount); - $baseCurrencyPaymentAmount = $paymentCurrencies->convertCurrency($paymentAmount, $paymentCurrency, $baseCurrency); + $baseCurrencyPaymentAmount = $paymentCurrencies->convertCurrency((float)$paymentAmount, $paymentCurrency, $baseCurrency); $baseCurrencyPaymentAmountAsCurrency = Craft::t('commerce', 'Pay {amount} of {currency} on the order.', ['amount' => Currency::formatAsCurrency($baseCurrencyPaymentAmount, $baseCurrency), 'currency' => $baseCurrency]); $outstandingBalance = $order->outstandingBalance; From 308c1e126a8a5c9fc9018fc7e8264ccf40006c6e Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Wed, 4 Dec 2024 18:51:03 +0800 Subject: [PATCH 44/69] Fixed Drafts showing up in inventory --- CHANGELOG.md | 1 + src/controllers/InventoryController.php | 2 ++ src/services/Inventory.php | 6 +++++- src/services/InventoryLocations.php | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6d67c23cc..e35f416765 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Fixed a bug where draft purchasables would show up on the Inventory page. - Fixed a bug where the price was not formatted correctly according to the locale in the payment model on the Order Edit screens. ([#3789](https://github.com/craftcms/commerce/issues/3789)) ## 5.2.7 - 2024-11 diff --git a/src/controllers/InventoryController.php b/src/controllers/InventoryController.php index 13e2653aea..7d083825f7 100644 --- a/src/controllers/InventoryController.php +++ b/src/controllers/InventoryController.php @@ -242,6 +242,8 @@ public function actionInventoryLevelsTableData(): Response $inventoryQuery->leftJoin(['purchasables' => Table::PURCHASABLES], '[[ii.purchasableId]] = [[purchasables.id]]'); $inventoryQuery->addGroupBy(['[[purchasables.description]]', '[[purchasables.sku]]']); + $inventoryQuery->andWhere(['not', ['elements.id' => null]]); + if ($search) { $inventoryQuery->andWhere(['or', ['like', 'purchasables.description', $search], ['like', 'purchasables.sku', $search]]); } diff --git a/src/services/Inventory.php b/src/services/Inventory.php index 93107c4fba..01ecd7a4be 100644 --- a/src/services/Inventory.php +++ b/src/services/Inventory.php @@ -262,8 +262,12 @@ public function getInventoryLevelQuery(?int $limit = null, ?int $offset = null, ->limit($limit) ->offset($offset); + $query->leftJoin( + ['elements' => CraftTable::ELEMENTS], + '[[ii.purchasableId]] = [[elements.id]] AND [[elements.draftId]] IS NULL AND [[elements.revisionId]] IS NULL' + ); + if (!$withTrashed) { - $query->leftJoin(['elements' => CraftTable::ELEMENTS], '[[ii.purchasableId]] = [[elements.id]]'); $query->andWhere(['elements.dateDeleted' => null]); } diff --git a/src/services/InventoryLocations.php b/src/services/InventoryLocations.php index c92e806797..31ead4e14f 100644 --- a/src/services/InventoryLocations.php +++ b/src/services/InventoryLocations.php @@ -283,6 +283,7 @@ private function _createInventoryLocationsQuery(bool $withTrashed = false): Quer 'dateCreated', 'dateUpdated', ]) + ->orderBy(['name' => SORT_ASC]) ->from([Table::INVENTORYLOCATIONS]); if (!$withTrashed) { From 2476f8174af13a822db72e3dd0030c5d6637f8c6 Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Wed, 4 Dec 2024 20:16:45 +0800 Subject: [PATCH 45/69] Dont get drafts --- src/services/Inventory.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/services/Inventory.php b/src/services/Inventory.php index 01ecd7a4be..0ffa7726d6 100644 --- a/src/services/Inventory.php +++ b/src/services/Inventory.php @@ -207,6 +207,7 @@ public function getInventoryLocationLevels(InventoryLocation $inventoryLocation, { $levels = $this->getInventoryLevelQuery(withTrashed: $withTrashed) ->andWhere(['inventoryLocationId' => $inventoryLocation->id]) + ->andWhere(['not', ['elements.id' => null]]) ->collect(); $inventoryItems = Plugin::getInstance()->getInventory()->getInventoryItemsByIds($levels->pluck('inventoryItemId')->unique()->toArray()); From 0c70bfaea1c2db4f7f74a0881e4212ef45958792 Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Wed, 4 Dec 2024 20:18:16 +0800 Subject: [PATCH 46/69] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e35f416765..bef1b09cdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased - Fixed a bug where draft purchasables would show up on the Inventory page. +- Fixed a PHP error that could occur when creating inventory transfers. - Fixed a bug where the price was not formatted correctly according to the locale in the payment model on the Order Edit screens. ([#3789](https://github.com/craftcms/commerce/issues/3789)) ## 5.2.7 - 2024-11 From cd2a61ab484944af1c318b0eee49472ef5d53e31 Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Wed, 4 Dec 2024 20:40:29 +0800 Subject: [PATCH 47/69] Fixed type error --- CHANGELOG.md | 2 +- src/collections/UpdateInventoryLevelCollection.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bef1b09cdc..c28b0bc97e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## Unreleased - Fixed a bug where draft purchasables would show up on the Inventory page. -- Fixed a PHP error that could occur when creating inventory transfers. +- Fixed a PHP error that could occur when creating inventory transfers. ([#3696](https://github.com/craftcms/commerce/issues/3696)) - Fixed a bug where the price was not formatted correctly according to the locale in the payment model on the Order Edit screens. ([#3789](https://github.com/craftcms/commerce/issues/3789)) ## 5.2.7 - 2024-11 diff --git a/src/collections/UpdateInventoryLevelCollection.php b/src/collections/UpdateInventoryLevelCollection.php index afbfe84f22..8c57c200a8 100644 --- a/src/collections/UpdateInventoryLevelCollection.php +++ b/src/collections/UpdateInventoryLevelCollection.php @@ -8,6 +8,7 @@ namespace craft\commerce\collections; use craft\commerce\models\inventory\UpdateInventoryLevel; +use craft\commerce\models\inventory\UpdateInventoryLevelInTransfer; use Illuminate\Support\Collection; /** @@ -46,7 +47,7 @@ public static function make($items = []) */ public function getPurchasables(): array { - return $this->map(function(UpdateInventoryLevel $updateInventoryLevel) { + return $this->map(function(UpdateInventoryLevel|UpdateInventoryLevelInTransfer $updateInventoryLevel) { return $updateInventoryLevel->inventoryItem->getPurchasable(); })->all(); } From 0cf2242f74f9c35f08ebcce9e2a84a8e161a21d3 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Wed, 4 Dec 2024 14:21:03 +0000 Subject: [PATCH 48/69] Match DB values to what is stored in PC --- .../m241204_091901_fix_store_environment_variables.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/migrations/m241204_091901_fix_store_environment_variables.php b/src/migrations/m241204_091901_fix_store_environment_variables.php index ea0d504579..279ac79e65 100644 --- a/src/migrations/m241204_091901_fix_store_environment_variables.php +++ b/src/migrations/m241204_091901_fix_store_environment_variables.php @@ -62,18 +62,12 @@ public function safeUp(): bool continue; } - // If the value in PC is a bool and the same as the DB value, skip it - if (in_array($storeSettingsForStore[$storeProperty], ['0', '1', 0, 1, false, true, 'false', 'true'], true) && $storeSettingsForStore[$storeProperty] == $store[$storeProperty]) { - continue; - } - - // If the value in PC is a string and is different from the DB value, skip it to avoid change in behavior + // Parse the value from the PC $envVarValue = App::parseBooleanEnv($storeSettingsForStore[$storeProperty]); - if ($envVarValue != $store[$storeProperty]) { + if ($envVarValue === null) { continue; } - // Else update the DB with the environment variable name $updateData[$storeProperty] = $storeSettingsForStore[$storeProperty]; } From 48e3dcb2915475cb62773b9b9742363c879aea50 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Wed, 4 Dec 2024 14:25:44 +0000 Subject: [PATCH 49/69] Update changelog [ci skip] --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3521bde6b2..c08e5a01ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ - Fixed a bug where draft purchasables would show up on the Inventory page. - Fixed a PHP error that could occur when creating inventory transfers. ([#3696](https://github.com/craftcms/commerce/issues/3696)) - Fixed a bug where the price was not formatted correctly according to the locale in the payment model on the Order Edit screens. ([#3789](https://github.com/craftcms/commerce/issues/3789)) -- Fixed a bug where it wasn’t possible to set store settings to environment variables. ([#3786](https://github.com/craftcms/commerce/issues/3786)) +- Fixed a bug where it store settings weren’t respecting environment variables. ([#3786](https://github.com/craftcms/commerce/issues/3786)) ## 5.2.7 - 2024-11 From 8b0cc543284adb660c9e6bd232dcf268b013725c Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Wed, 4 Dec 2024 16:15:03 +0000 Subject: [PATCH 50/69] Fixed typecasting `isLite` causing deprecation log --- src/models/ShippingMethod.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/models/ShippingMethod.php b/src/models/ShippingMethod.php index 79fc4edd50..b9f951390c 100644 --- a/src/models/ShippingMethod.php +++ b/src/models/ShippingMethod.php @@ -38,7 +38,6 @@ public function behaviors(): array 'name' => AttributeTypecastBehavior::TYPE_STRING, 'handle' => AttributeTypecastBehavior::TYPE_STRING, 'enabled' => AttributeTypecastBehavior::TYPE_BOOLEAN, - 'isLite' => AttributeTypecastBehavior::TYPE_BOOLEAN, ], ]; From 4f57dc1f3691bed9900ce4b198ab7e918e9a1f08 Mon Sep 17 00:00:00 2001 From: brandonkelly Date: Wed, 4 Dec 2024 10:47:43 -0800 Subject: [PATCH 51/69] Changelog tweaks --- CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c08e5a01ee..a764a4d16b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,13 @@ ## Unreleased -- Fixed a bug where purchasable line items were missing their CP edit URL on the Edit Order page. ([#3792](https://github.com/craftcms/commerce/issues/3792)) -- Fixed a bug where draft purchasables would show up on the Inventory page. +- Fixed a bug where line items weren’t getting hyperlinked within Edit Order pages. ([#3792](https://github.com/craftcms/commerce/issues/3792)) +- Fixed a bug where Inventory pages were showing draft purchasables. - Fixed a PHP error that could occur when creating inventory transfers. ([#3696](https://github.com/craftcms/commerce/issues/3696)) -- Fixed a bug where the price was not formatted correctly according to the locale in the payment model on the Order Edit screens. ([#3789](https://github.com/craftcms/commerce/issues/3789)) -- Fixed a bug where it store settings weren’t respecting environment variables. ([#3786](https://github.com/craftcms/commerce/issues/3786)) +- Fixed a bug where prices weren’t getting formatted per the user’s formatting locale, in payment models on Edit Order pages. ([#3789](https://github.com/craftcms/commerce/issues/3789)) +- Fixed a bug where store settings weren’t respecting environment variables. ([#3786](https://github.com/craftcms/commerce/issues/3786)) -## 5.2.7 - 2024-11 +## 5.2.7 - 2024-12-02 - Fixed an error that occurred on the Orders index page when running Craft CMS 5.5.4 or later. ([#3793](https://github.com/craftcms/commerce/issues/3793)) - Fixed a bug where a structured product type’s “Max Levels” setting wasn’t being respected. ([#3785](https://github.com/craftcms/commerce/issues/3785)) From bf0c75fe823d556bc08e58bd9e174c5bcc17b374 Mon Sep 17 00:00:00 2001 From: brandonkelly Date: Wed, 4 Dec 2024 10:48:07 -0800 Subject: [PATCH 52/69] Finish 5.2.8 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a764a4d16b..a30de6c0dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Release Notes for Craft Commerce -## Unreleased +## 5.2.8 - 2024-12-04 - Fixed a bug where line items weren’t getting hyperlinked within Edit Order pages. ([#3792](https://github.com/craftcms/commerce/issues/3792)) - Fixed a bug where Inventory pages were showing draft purchasables. From 5daeac9d8dc6a698c22e8397882cf07d22c93b62 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Thu, 5 Dec 2024 11:03:25 +0000 Subject: [PATCH 53/69] WIP inventory item steamline --- src/base/InventoryItemTrait.php | 61 ++++++++++++++ src/base/InventoryLocationTrait.php | 61 ++++++++++++++ src/base/InventoryMovement.php | 23 ++--- src/base/InventoryMovementInterface.php | 2 +- src/base/Purchasable.php | 64 ++++++++++++++ src/base/StoreTrait.php | 5 ++ .../UpdateInventoryLevelCollection.php | 5 +- src/controllers/InventoryController.php | 77 +++++++---------- src/controllers/OrdersController.php | 4 +- src/controllers/TransfersController.php | 6 +- src/elements/Transfer.php | 36 ++++---- .../inventory/InventoryManualMovement.php | 4 +- .../inventory/InventoryTransferMovement.php | 8 -- src/models/inventory/UpdateInventoryLevel.php | 15 +--- .../UpdateInventoryLevelInTransfer.php | 14 +--- src/services/Inventory.php | 83 ++++++++++--------- src/services/InventoryLocations.php | 2 +- 17 files changed, 319 insertions(+), 151 deletions(-) create mode 100644 src/base/InventoryItemTrait.php create mode 100644 src/base/InventoryLocationTrait.php diff --git a/src/base/InventoryItemTrait.php b/src/base/InventoryItemTrait.php new file mode 100644 index 0000000000..1bfc485d20 --- /dev/null +++ b/src/base/InventoryItemTrait.php @@ -0,0 +1,61 @@ + + * @since 5.3.0 + */ +trait InventoryItemTrait +{ + /** + * @var int|null The inventory item ID + */ + public ?int $inventoryItemId = null; + + /** + * @var InventoryItem|null The inventory item + * @see getInventoryItem() + * @see setInventoryItem() + */ + private ?InventoryItem $_inventoryItem = null; + + /** + * @param InventoryItem|null $inventoryItem + * @return void + */ + public function setInventoryItem(?InventoryItem $inventoryItem): void + { + $this->_inventoryItem = $inventoryItem; + $this->inventoryItemId = $inventoryItem?->id ?? null; + } + + /** + * @return InventoryItem|null + * @throws \yii\base\InvalidConfigException + */ + public function getInventoryItem(): ?InventoryItem + { + if (isset($this->_inventoryItem)) { + return $this->_inventoryItem; + } + + if ($this->inventoryItemId) { + $this->_inventoryItem = Plugin::getInstance()->getInventory()->getInventoryItemById($this->inventoryItemId); + + return $this->_inventoryItem; + } + + return null; + } +} diff --git a/src/base/InventoryLocationTrait.php b/src/base/InventoryLocationTrait.php new file mode 100644 index 0000000000..02888bc927 --- /dev/null +++ b/src/base/InventoryLocationTrait.php @@ -0,0 +1,61 @@ + + * @since 5.3.0 + */ +trait InventoryLocationTrait +{ + /** + * @var int|null The inventory item ID + */ + public ?int $inventoryLocationId = null; + + /** + * @var InventoryLocation|null The inventory item + * @see getInventoryLocation() + * @see setInventoryLocation() + */ + private ?InventoryLocation $_inventoryLocation = null; + + /** + * @param InventoryLocation|null $inventoryLocation + * @return void + */ + public function setInventoryLocation(?InventoryLocation $inventoryLocation): void + { + $this->_inventoryLocation = $inventoryLocation; + $this->inventoryLocationId = $inventoryLocation?->id ?? null; + } + + /** + * @return InventoryLocation|null + * @throws \yii\base\InvalidConfigException + */ + public function getInventoryLocation(): ?InventoryLocation + { + if (isset($this->_inventoryLocation)) { + return $this->_inventoryLocation; + } + + if ($this->inventoryLocationId) { + $this->_inventoryLocation = Plugin::getInstance()->getInventoryLocations()->getInventoryLocationById($this->inventoryLocationId); + + return $this->_inventoryLocation; + } + + return null; + } +} diff --git a/src/base/InventoryMovement.php b/src/base/InventoryMovement.php index 6675a1ff48..f858d301a1 100644 --- a/src/base/InventoryMovement.php +++ b/src/base/InventoryMovement.php @@ -18,10 +18,7 @@ */ abstract class InventoryMovement extends Model implements InventoryMovementInterface { - /** - * @var InventoryItem The inventory item - */ - public InventoryItem $inventoryItem; + use InventoryItemTrait; /** * @var InventoryLocation @@ -90,27 +87,31 @@ public function init(): void } /** - * @inheritDoc + * @return array */ - public function isValid(): bool + protected function defineRules(): array { - return $this->validate(); + $rules = parent::defineRules(); + + $rules[] = [['inventoryItemId'], 'safe']; + + return $rules; } /** * @inheritDoc */ - public function getInventoryMovementHash(): string + public function isValid(): bool { - return $this->_inventoryMovementHash; + return $this->validate(); } /** * @inheritDoc */ - public function getInventoryItem(): InventoryItem + public function getInventoryMovementHash(): string { - return $this->inventoryItem; + return $this->_inventoryMovementHash; } /** diff --git a/src/base/InventoryMovementInterface.php b/src/base/InventoryMovementInterface.php index 2ea87f0c0a..7e29090bad 100644 --- a/src/base/InventoryMovementInterface.php +++ b/src/base/InventoryMovementInterface.php @@ -21,7 +21,7 @@ interface InventoryMovementInterface /** * @return InventoryItem */ - public function getInventoryItem(): InventoryItem; + public function getInventoryItem(): ?InventoryItem; /** * @return InventoryLocation diff --git a/src/base/Purchasable.php b/src/base/Purchasable.php index ce7906982c..1701e4981e 100644 --- a/src/base/Purchasable.php +++ b/src/base/Purchasable.php @@ -10,12 +10,14 @@ use Craft; use craft\base\Element; use craft\base\NestedElementInterface; +use craft\commerce\collections\UpdateInventoryLevelCollection; use craft\commerce\db\Table; use craft\commerce\elements\Order; use craft\commerce\errors\StoreNotFoundException; use craft\commerce\helpers\Currency; use craft\commerce\helpers\Localization; use craft\commerce\helpers\Purchasable as PurchasableHelper; +use craft\commerce\models\inventory\UpdateInventoryLevel; use craft\commerce\models\InventoryItem; use craft\commerce\models\InventoryLevel; use craft\commerce\models\LineItem; @@ -949,6 +951,68 @@ public function getStock(): int return $this->_stock; } + /** + * @return void + * @since 5.3.0 + */ + public function setStockLevel($amount): void + { + + // $commercePlugin = Plugin::getInstance(); + // + // // For simplicity let's just get the first inventory location + // $inventoryLocation = $commercePlugin->getInventoryLocations()->getAllInventoryLocations()->first(); + // + // // Retrieve the inventory item ID for the variants you want to update + // $variantInventoryItemQuery = \craft\commerce\elements\Variant::find() + // // ->select(['inventoryitems.id as inventoryItemId']); + // ->select(['inventoryitems.id as inventoryItemId']) + // ->andWhere(['elements.id' => 327]); + // // Can add extra criteria to the query if needed + // + // // Retrieve the inventory items for the variants + // $inventoryItems = $commercePlugin->getInventory()->getInventoryItemQuery() + // ->andWhere(['id' => $variantInventoryItemQuery]) + // ->collect() + // ->map(function($inventoryItem) { + // return new InventoryItem($inventoryItem); + // }); + // + // // Retrieve the inventory levels for the inventory items + // $inventoryLevels = $commercePlugin->getInventory()->getInventoryLevelQuery() + // ->andWhere(['ii.id' => $variantInventoryItemQuery]) + // ->andWhere(['it.inventoryLocationId' => $inventoryLocation->id]) + // ->collect() + // ->map(function($inventoryLevel) { + // unset($inventoryLevel['purchasableId']); + // return new InventoryLevel($inventoryLevel); + // }); + // + // $updateInventoryLevels = UpdateInventoryLevelCollection::make(); + // + // $inventoryLevels->each(function(InventoryLevel $inventoryLevel) use ($updateInventoryLevels, $inventoryItems, $inventoryLocation) { + // // Update action can be `SET` or `ADJUST` based on inserting or updating the stock + // $updateAction = \craft\commerce\enums\InventoryUpdateQuantityType::SET; + // + // // Stock amount, this would need to be based on your requirements/data being imported + // $stock = 1; // stock amount + // + // $updateInventoryLevels->push(new UpdateInventoryLevel([ + // 'type' => InventoryTransactionType::AVAILABLE->value, + // 'updateAction' => $updateAction, + // 'inventoryItem' => $inventoryItems->firstWhere('id', $inventoryLevel->inventoryItemId), + // 'inventoryLocation' => $inventoryLocation, + // 'quantity' => $stock, + // 'note' => '', + // ]) + // ); + // }); + // + // if ($updateInventoryLevels->count() > 0) { + // $commercePlugin->getInventory()->executeUpdateInventoryLevels($updateInventoryLevels); + // } + } + /** * Returns the total stock across all locations this purchasable is tracked in. diff --git a/src/base/StoreTrait.php b/src/base/StoreTrait.php index 8cec683c28..cc92f91216 100644 --- a/src/base/StoreTrait.php +++ b/src/base/StoreTrait.php @@ -1,4 +1,9 @@ map(function(UpdateInventoryLevel $updateInventoryLevel) { - return $updateInventoryLevel->inventoryItem->getPurchasable(); + return $this->map(function(UpdateInventoryLevel|UpdateInventoryLevelInTransfer $updateInventoryLevel) { + return $updateInventoryLevel->getInventoryItem()->getPurchasable(); })->all(); } } diff --git a/src/controllers/InventoryController.php b/src/controllers/InventoryController.php index 7d083825f7..8ebfb7d3af 100644 --- a/src/controllers/InventoryController.php +++ b/src/controllers/InventoryController.php @@ -503,7 +503,6 @@ public function actionUpdateLevels(): Response $note = Craft::$app->getRequest()->getRequiredParam('note'); $inventoryLocationId = (int)Craft::$app->getRequest()->getRequiredParam('inventoryLocationId'); $inventoryItemIds = Craft::$app->getRequest()->getRequiredParam('ids'); - $inventoryLocation = Plugin::getInstance()->getInventoryLocations()->getInventoryLocationById($inventoryLocationId); $type = Craft::$app->getRequest()->getRequiredParam('type'); // We don't add zero amounts as transactions movements @@ -514,17 +513,16 @@ public function actionUpdateLevels(): Response $errors = []; $updateInventoryLevels = UpdateInventoryLevelCollection::make(); foreach ($inventoryItemIds as $inventoryItemId) { - $inventoryItem = Plugin::getInstance()->getInventory()->getInventoryItemById($inventoryItemId); - - $updateInventoryLevels->push(new UpdateInventoryLevel([ - 'type' => $type, - 'updateAction' => $updateAction, - 'inventoryItem' => $inventoryItem, - 'inventoryLocation' => $inventoryLocation, - 'quantity' => $quantity, - 'note' => $note, - ]) - ); + // Verbosely set property to show usages + $updateInventoryLevel = new UpdateInventoryLevel(); + $updateInventoryLevel->type = $type; + $updateInventoryLevel->updateAction = $updateAction; + $updateInventoryLevel->inventoryItemId = $inventoryItemId; + $updateInventoryLevel->inventoryLocationId = $inventoryLocationId; + $updateInventoryLevel->quantity = $quantity; + $updateInventoryLevel->note = $note; + + $updateInventoryLevels->push($updateInventoryLevel); } @@ -540,7 +538,8 @@ public function actionUpdateLevels(): Response $resultingInventoryLevels = []; foreach ($updateInventoryLevels as $updateInventoryLevel) { - $resultingInventoryLevels[] = Plugin::getInstance()->getInventory()->getInventoryLevel($updateInventoryLevel->inventoryItem, $updateInventoryLevel->inventoryLocation); + /** @var UpdateInventoryLevel $updateInventoryLevel */ + $resultingInventoryLevels[] = Plugin::getInstance()->getInventory()->getInventoryLevel($updateInventoryLevel->inventoryItemId, $updateInventoryLevel->inventoryLocationId); } return $this->asSuccess(Craft::t('commerce', 'Inventory updated.'), [ @@ -565,12 +564,9 @@ public function actionEditUpdateLevelsModal(): Response $quantity = (int)$this->request->getParam('quantity', 0); $type = $this->request->getRequiredParam('type'); - $inventoryLocation = Plugin::getInstance()->getInventoryLocations()->getInventoryLocationById($inventoryLocationId); - $inventoryLevels = []; foreach ($inventoryItemIds as $inventoryItemId) { - $item = Plugin::getInstance()->getInventory()->getInventoryItemById((int)$inventoryItemId); - $inventoryLevels[] = Plugin::getInstance()->getInventory()->getInventoryLevel($item, $inventoryLocation); + $inventoryLevels[] = Plugin::getInstance()->getInventory()->getInventoryLevel((int)$inventoryItemId, $inventoryLocationId); } $params = [ @@ -614,17 +610,14 @@ public function actionSaveInventoryMovement(): Response return $this->asSuccess(Craft::t('commerce', 'No inventory movements made.')); } - $inventoryMovement = new InventoryManualMovement( - [ - 'inventoryItem' => Plugin::getInstance()->getInventory()->getInventoryItemById($inventoryItemId), - 'fromInventoryLocation' => Plugin::getInstance()->getInventoryLocations()->getInventoryLocationById($fromInventoryLocationId), - 'toInventoryLocation' => Plugin::getInstance()->getInventoryLocations()->getInventoryLocationById($toInventoryLocationId), - 'fromInventoryTransactionType' => InventoryTransactionType::from($fromInventoryTransactionType), - 'toInventoryTransactionType' => InventoryTransactionType::from($toInventoryTransactionType), - 'quantity' => $quantity, - 'note' => $note, - ] - ); + $inventoryMovement = new InventoryManualMovement(); + $inventoryMovement->inventoryItemId = $inventoryItemId; + $inventoryMovement->fromInventoryLocation = Plugin::getInstance()->getInventoryLocations()->getInventoryLocationById($fromInventoryLocationId); + $inventoryMovement->toInventoryLocation = Plugin::getInstance()->getInventoryLocations()->getInventoryLocationById($toInventoryLocationId); + $inventoryMovement->fromInventoryTransactionType = InventoryTransactionType::from($fromInventoryTransactionType); + $inventoryMovement->toInventoryTransactionType = InventoryTransactionType::from($toInventoryTransactionType); + $inventoryMovement->quantity = $quantity; + $inventoryMovement->note = $note; if ($inventoryMovement->validate()) { /** @var InventoryMovementCollection $inventoryMovementCollection */ @@ -665,19 +658,16 @@ public function actionEditMovementModal(): Response $toInventoryTransactionType = $toInventoryTransactionType->value; } - $inventoryMovement = new InventoryManualMovement( - [ - 'inventoryItem' => Plugin::getInstance()->getInventory()->getInventoryItemById($inventoryItemId), - 'fromInventoryLocation' => Plugin::getInstance()->getInventoryLocations()->getInventoryLocationById($fromInventoryLocationId), - 'toInventoryLocation' => Plugin::getInstance()->getInventoryLocations()->getInventoryLocationById($toInventoryLocationId), - 'fromInventoryTransactionType' => InventoryTransactionType::from($fromInventoryTransactionType), - 'toInventoryTransactionType' => InventoryTransactionType::from($toInventoryTransactionType), - 'quantity' => $quantity, - 'note' => $note, - ] - ); - - $fromLevel = Plugin::getInstance()->getInventory()->getInventoryLevel($inventoryMovement->inventoryItem, $inventoryMovement->fromInventoryLocation); + $inventoryMovement = new InventoryManualMovement(); + $inventoryMovement->inventoryItemId = $inventoryItemId; + $inventoryMovement->fromInventoryLocation = Plugin::getInstance()->getInventoryLocations()->getInventoryLocationById($fromInventoryLocationId); + $inventoryMovement->toInventoryLocation = Plugin::getInstance()->getInventoryLocations()->getInventoryLocationById($toInventoryLocationId); + $inventoryMovement->fromInventoryTransactionType = InventoryTransactionType::from($fromInventoryTransactionType); + $inventoryMovement->toInventoryTransactionType = InventoryTransactionType::from($toInventoryTransactionType); + $inventoryMovement->quantity = $quantity; + $inventoryMovement->note = $note; + + $fromLevel = Plugin::getInstance()->getInventory()->getInventoryLevel($inventoryMovement->inventoryItemId, $inventoryMovement->fromInventoryLocation); $fromTotal = $fromLevel->{$fromInventoryTransactionType . 'Total'}; $movableTo = $movableTo->toArray(); @@ -705,10 +695,7 @@ public function actionUnfulfilledOrders(): Response $inventoryLocationId = Craft::$app->getRequest()->getParam('inventoryLocationId'); $inventoryItemId = Craft::$app->getRequest()->getParam('inventoryItemId'); - $inventoryLocation = Plugin::getInstance()->getInventoryLocations()->getInventoryLocationById($inventoryLocationId); - $inventoryItem = Plugin::getInstance()->getInventory()->getInventoryItemById($inventoryItemId); - - $orders = Plugin::getInstance()->getInventory()->getUnfulfilledOrders($inventoryItem, $inventoryLocation); + $orders = Plugin::getInstance()->getInventory()->getUnfulfilledOrders($inventoryItemId, $inventoryLocationId); $title = Craft::t('commerce', '{count} Unfulfilled Orders', [ 'count' => count($orders), diff --git a/src/controllers/OrdersController.php b/src/controllers/OrdersController.php index 390a0b9b6a..5da4a7fc34 100644 --- a/src/controllers/OrdersController.php +++ b/src/controllers/OrdersController.php @@ -259,10 +259,10 @@ public function actionFulfill(): Response $qty = (int)$fulfillment['quantity']; if ($qty != 0) { $inventoryLocation = Plugin::getInstance()->getInventoryLocations()->getInventoryLocationById($fulfillment['inventoryLocationId']); - $inventoryItem = Plugin::getInstance()->getInventory()->getInventoryItemById($fulfillment['inventoryItemId']); + $movement = new InventoryFulfillMovement(); $movement->fromInventoryLocation = $inventoryLocation; - $movement->inventoryItem = $inventoryItem; + $movement->inventoryItemId = $fulfillment['inventoryItemId']; $movement->toInventoryLocation = $inventoryLocation; $movement->fromInventoryTransactionType = InventoryTransactionType::COMMITTED; $movement->toInventoryTransactionType = InventoryTransactionType::FULFILLED; diff --git a/src/controllers/TransfersController.php b/src/controllers/TransfersController.php index a6fcf304ba..0c278fdb69 100644 --- a/src/controllers/TransfersController.php +++ b/src/controllers/TransfersController.php @@ -197,7 +197,7 @@ public function actionReceiveTransfer(): Response $inventoryAcceptedMovement = new InventoryTransferMovement(); $inventoryAcceptedMovement->quantity = $acceptedAmount; $inventoryAcceptedMovement->transferId = $transfer->id; - $inventoryAcceptedMovement->inventoryItem = $detail->getInventoryItem(); + $inventoryAcceptedMovement->setInventoryItem($detail->getInventoryItem()); $inventoryAcceptedMovement->toInventoryLocation = $transfer->getDestinationLocation(); $inventoryAcceptedMovement->fromInventoryLocation = $transfer->getDestinationLocation(); // we are moving from incoming to available $inventoryAcceptedMovement->toInventoryTransactionType = InventoryTransactionType::AVAILABLE; @@ -213,9 +213,9 @@ public function actionReceiveTransfer(): Response $inventoryRejectedMovement = new UpdateInventoryLevel(); $inventoryRejectedMovement->quantity = $rejectedAmount * -1; $inventoryRejectedMovement->updateAction = InventoryUpdateQuantityType::ADJUST; - $inventoryRejectedMovement->inventoryItem = $detail->getInventoryItem(); + $inventoryRejectedMovement->inventoryItemId = $detail->inventoryItemId; $inventoryRejectedMovement->transferId = $transfer->id; - $inventoryRejectedMovement->inventoryLocation = $transfer->getDestinationLocation(); + $inventoryRejectedMovement->setInventoryLocation($transfer->getDestinationLocation()); $inventoryRejectedMovement->type = InventoryTransactionType::INCOMING->value; $inventoryUpdateCollection->push($inventoryRejectedMovement); diff --git a/src/elements/Transfer.php b/src/elements/Transfer.php index f59e49e689..e13ed7d21e 100644 --- a/src/elements/Transfer.php +++ b/src/elements/Transfer.php @@ -779,26 +779,26 @@ public function afterSave(bool $isNew): void if ($this->getTransferStatus() === TransferStatusType::PENDING && $originalTransferStatus == TransferStatusType::DRAFT->value) { $inventoryUpdateCollection = new UpdateInventoryLevelCollection(); foreach ($this->getDetails() as $detail) { - $inventoryUpdate1 = new UpdateInventoryLevelInTransfer([ - 'type' => InventoryTransactionType::INCOMING->value, - 'updateAction' => InventoryUpdateQuantityType::ADJUST, - 'inventoryItem' => $detail->getInventoryItem(), - 'transferId' => $this->id, - 'inventoryLocation' => $this->getDestinationLocation(), - 'quantity' => $detail->quantity, - 'note' => Craft::t('commerce', 'Incoming transfer from Transfer ID: ') . $this->id, - ]); + $inventoryUpdate1 = new UpdateInventoryLevelInTransfer(); + $inventoryUpdate1->type = InventoryTransactionType::INCOMING->value; + $inventoryUpdate1->updateAction = InventoryUpdateQuantityType::ADJUST; + $inventoryUpdate1->inventoryItemId = $detail->inventoryItemId; + $inventoryUpdate1->transferId = $this->id; + $inventoryUpdate1->inventoryLocationId = $this->destinationLocationId; + $inventoryUpdate1->quantity = $detail->quantity; + $inventoryUpdate1->note = Craft::t('commerce', 'Incoming transfer from Transfer ID: ') . $this->id; + $inventoryUpdateCollection->push($inventoryUpdate1); - $inventoryUpdate2 = new UpdateInventoryLevelInTransfer([ - 'type' => 'onHand', - 'updateAction' => InventoryUpdateQuantityType::ADJUST, - 'inventoryItem' => $detail->getInventoryItem(), - 'transferId' => $this->id, - 'inventoryLocation' => $this->getOriginLocation(), - 'quantity' => $detail->quantity * -1, - 'note' => Craft::t('commerce', 'Outgoing transfer from Transfer ID: ') . $this->id, - ]); + $inventoryUpdate2 = new UpdateInventoryLevelInTransfer(); + $inventoryUpdate2->type = 'onHand'; + $inventoryUpdate2->updateAction = InventoryUpdateQuantityType::ADJUST; + $inventoryUpdate2->inventoryItemId = $detail->inventoryItemId; + $inventoryUpdate2->transferId = $this->id; + $inventoryUpdate2->inventoryLocationId = $this->originLocationId; + $inventoryUpdate2->quantity = $detail->quantity * -1; + $inventoryUpdate2->note = Craft::t('commerce', 'Outgoing transfer from Transfer ID: ') . $this->id; + $inventoryUpdateCollection->push($inventoryUpdate2); } diff --git a/src/models/inventory/InventoryManualMovement.php b/src/models/inventory/InventoryManualMovement.php index e3e98f5b3d..7d0ed0a7ec 100644 --- a/src/models/inventory/InventoryManualMovement.php +++ b/src/models/inventory/InventoryManualMovement.php @@ -83,7 +83,7 @@ public function fromLocationAfterQuantity(): int ->from(Table::INVENTORYTRANSACTIONS) ->where([ 'type' => $this->fromInventoryTransactionType->value, - 'inventoryItemId' => $this->inventoryItem->id, + 'inventoryItemId' => $this->inventoryItemId, 'inventoryLocationId' => $this->fromInventoryLocation->id, ]) ->params([':quantity' => $this->quantity]) @@ -112,7 +112,7 @@ public function toLocationAfterQuantity(): int ->from(Table::INVENTORYTRANSACTIONS) ->where([ 'type' => $this->toInventoryTransactionType->value, - 'inventoryItemId' => $this->inventoryItem->id, + 'inventoryItemId' => $this->inventoryItemId, 'inventoryLocationId' => $this->toInventoryLocation->id, ]) ->params([':quantity' => $this->quantity]) diff --git a/src/models/inventory/InventoryTransferMovement.php b/src/models/inventory/InventoryTransferMovement.php index 558c52cc13..cbe7d8506d 100644 --- a/src/models/inventory/InventoryTransferMovement.php +++ b/src/models/inventory/InventoryTransferMovement.php @@ -9,12 +9,4 @@ */ class InventoryTransferMovement extends InventoryMovement { - /** - * @return array - */ - public function defineRules(): array - { - $rules = parent::defineRules(); - return $rules; - } } diff --git a/src/models/inventory/UpdateInventoryLevel.php b/src/models/inventory/UpdateInventoryLevel.php index 57f8574059..c0e3986279 100644 --- a/src/models/inventory/UpdateInventoryLevel.php +++ b/src/models/inventory/UpdateInventoryLevel.php @@ -3,10 +3,11 @@ namespace craft\commerce\models\inventory; use craft\base\Model; +use craft\commerce\base\InventoryItemTrait; +use craft\commerce\base\InventoryLocationTrait; use craft\commerce\enums\InventoryTransactionType; use craft\commerce\enums\InventoryUpdateQuantityType; use craft\commerce\models\InventoryItem; -use craft\commerce\models\InventoryLocation; /** * Update (Set and Adjust) Inventory Quantity model @@ -15,6 +16,8 @@ */ class UpdateInventoryLevel extends Model { + use InventoryItemTrait, InventoryLocationTrait; + /** * The type is the set of InventoryTransactionType values, plus the `onHand` type. * @var string The inventory update type. @@ -32,16 +35,6 @@ class UpdateInventoryLevel extends Model */ public InventoryUpdateQuantityType $updateAction; - /** - * @var InventoryItem The inventory item - */ - public InventoryItem $inventoryItem; - - /** - * @var InventoryLocation The inventory location. - */ - public InventoryLocation $inventoryLocation; - /** * @var int The quantity to update. */ diff --git a/src/models/inventory/UpdateInventoryLevelInTransfer.php b/src/models/inventory/UpdateInventoryLevelInTransfer.php index 675e25da1e..553e40eb17 100644 --- a/src/models/inventory/UpdateInventoryLevelInTransfer.php +++ b/src/models/inventory/UpdateInventoryLevelInTransfer.php @@ -3,6 +3,8 @@ namespace craft\commerce\models\inventory; use craft\base\Model; +use craft\commerce\base\InventoryItemTrait; +use craft\commerce\base\InventoryLocationTrait; use craft\commerce\enums\InventoryTransactionType; use craft\commerce\enums\InventoryUpdateQuantityType; use craft\commerce\models\InventoryItem; @@ -15,6 +17,8 @@ */ class UpdateInventoryLevelInTransfer extends Model { + use InventoryItemTrait, InventoryLocationTrait; + /** * The type is the set of InventoryTransactionType values, plus the `onHand` type. * @var string The inventory update type. @@ -32,16 +36,6 @@ class UpdateInventoryLevelInTransfer extends Model */ public InventoryUpdateQuantityType $updateAction; - /** - * @var InventoryItem The inventory item - */ - public InventoryItem $inventoryItem; - - /** - * @var InventoryLocation The inventory location. - */ - public InventoryLocation $inventoryLocation; - /** * @var int The quantity to update. */ diff --git a/src/services/Inventory.php b/src/services/Inventory.php index 0ffa7726d6..a3324876a0 100644 --- a/src/services/Inventory.php +++ b/src/services/Inventory.php @@ -61,7 +61,7 @@ public function getInventoryLevelsForPurchasable(Purchasable $purchasable): Coll $storeInventoryLocations = Plugin::getInstance()->getInventoryLocations()->getInventoryLocations($storeId); foreach ($storeInventoryLocations as $inventoryLocation) { - $inventoryLevel = $this->getInventoryLevel($purchasable->getInventoryItem(), $inventoryLocation); + $inventoryLevel = $this->getInventoryLevel($purchasable->inventoryItemId, $inventoryLocation->id); if (!$inventoryLevel) { continue; @@ -115,17 +115,20 @@ public function getInventoryItemsByIds(array $ids): Collection /** * Returns an inventory level model which is the sum of all inventory movements types for an item in a location. * - * @param InventoryItem $inventoryItem - * @param InventoryLocation $inventoryLocation + * @param InventoryItem|int $inventoryItem + * @param InventoryLocation|int $inventoryLocation * @param bool $withTrashed * @return ?InventoryLevel */ - public function getInventoryLevel(InventoryItem $inventoryItem, InventoryLocation $inventoryLocation, bool $withTrashed = false): ?InventoryLevel + public function getInventoryLevel(InventoryItem|int $inventoryItem, InventoryLocation|int $inventoryLocation, bool $withTrashed = false): ?InventoryLevel { + $inventoryItemId = $inventoryItem instanceof InventoryItem ? $inventoryItem->id : $inventoryItem; + $inventoryLocationId = $inventoryLocation instanceof InventoryLocation ? $inventoryLocation->id : $inventoryLocation; + $result = $this->getInventoryLevelQuery(withTrashed: $withTrashed) ->andWhere([ - 'inventoryLocationId' => $inventoryLocation->id, - 'inventoryItemId' => $inventoryItem->id, + 'inventoryLocationId' => $inventoryLocationId, + 'inventoryItemId' => $inventoryItemId, ])->one(); if (!$result) { @@ -340,8 +343,8 @@ private function _setInventoryLevel(UpdateInventoryLevel|UpdateInventoryLevelInT ->from($tableName) ->where([ 'type' => $types, - 'inventoryItemId' => $updateInventoryLevel->inventoryItem->id, - 'inventoryLocationId' => $updateInventoryLevel->inventoryLocation->id, + 'inventoryItemId' => $updateInventoryLevel->inventoryItemId, + 'inventoryLocationId' => $updateInventoryLevel->inventoryLocationId, ]) ->params([':quantity' => $updateInventoryLevel->quantity]) ->scalar(); @@ -354,8 +357,8 @@ private function _setInventoryLevel(UpdateInventoryLevel|UpdateInventoryLevelInT $data = [ 'quantity' => $quantityQuery, 'type' => $type, - 'inventoryItemId' => $updateInventoryLevel->inventoryItem->id, - 'inventoryLocationId' => $updateInventoryLevel->inventoryLocation->id, + 'inventoryItemId' => $updateInventoryLevel->inventoryItemId, + 'inventoryLocationId' => $updateInventoryLevel->inventoryLocationId, 'note' => $updateInventoryLevel->note, 'movementHash' => $this->getMovementHash(), 'dateCreated' => Db::prepareDateForDb(new \DateTime()), @@ -389,8 +392,8 @@ private function _adjustInventoryLevel(UpdateInventoryLevel|UpdateInventoryLevel ->insert($tableName, [ 'quantity' => $updateInventoryLevel->quantity, 'type' => $type, - 'inventoryItemId' => $updateInventoryLevel->inventoryItem->id, - 'inventoryLocationId' => $updateInventoryLevel->inventoryLocation->id, + 'inventoryItemId' => $updateInventoryLevel->inventoryItemId, + 'inventoryLocationId' => $updateInventoryLevel->inventoryLocationId, 'movementHash' => $this->getMovementHash(), 'dateCreated' => Db::prepareDateForDb(new \DateTime()), 'note' => $updateInventoryLevel->note, @@ -488,13 +491,16 @@ public function getMovementHash(): string } /** - * @param InventoryItem $inventoryItem - * @param InventoryLocation $inventoryLocation + * @param InventoryItem|int $inventoryItem + * @param InventoryLocation|int $inventoryLocation * @return array */ - public function getUnfulfilledOrders(InventoryItem $inventoryItem, InventoryLocation $inventoryLocation): array + public function getUnfulfilledOrders(InventoryItem|int $inventoryItem, InventoryLocation|int $inventoryLocation): array { - $inventoryLevel = $this->getInventoryLevel($inventoryItem, $inventoryLocation); + $inventoryItemId = $inventoryItem instanceof InventoryItem ? $inventoryItem->id : $inventoryItem; + $inventoryLocationId = $inventoryLocation instanceof InventoryLocation ? $inventoryLocation->id : $inventoryLocation; + + $inventoryLevel = $this->getInventoryLevel($inventoryItemId, $inventoryLocationId); if ($inventoryLevel->committedTotal <= 0) { return []; @@ -507,8 +513,8 @@ public function getUnfulfilledOrders(InventoryItem $inventoryItem, InventoryLoca ->leftJoin(['orders' => Table::ORDERS], '[[lineItems.orderId]] = [[orders.id]]') ->leftJoin(['it' => Table::INVENTORYTRANSACTIONS], '[[it.lineItemId]] = [[lineItems.id]]') ->where(['orders.isCompleted' => true]) - ->andWhere(['it.inventoryItemId' => $inventoryItem->id]) - ->andWhere(['it.inventoryLocationId' => $inventoryLocation->id]) + ->andWhere(['it.inventoryItemId' => $inventoryItemId]) + ->andWhere(['it.inventoryLocationId' => $inventoryLocationId]) ->andWhere(['it.type' => InventoryTransactionType::COMMITTED->value]) ->groupBy(['lineItems.orderId', 'lineItems.id']) ->having(['>=', 'SUM(it.quantity)', 'lineItems.qty']) @@ -620,6 +626,7 @@ public function getInventoryFulfillmentLevels(Order $order): Collection */ public function orderCompleteHandler(Order $order) { + /** @var Collection[] $allInventoryLevels */ $allInventoryLevels = []; $qtyLineItem = []; foreach ($order->getLineItems() as $lineItem) { @@ -642,7 +649,7 @@ public function orderCompleteHandler(Order $order) $selectedInventoryLevelForItem = []; /** * @var int $purchasableId - * @var InventoryLevel $inventoryLevels + * @var Collection $inventoryLevels */ foreach ($allInventoryLevels as $purchasableId => $inventoryLevels) { foreach ($inventoryLevels as $level) { @@ -685,15 +692,16 @@ public function orderCompleteHandler(Order $order) $availableTotalByPurchasableIdAndLocationId[$lineItem->purchasableId . '-' . $level->inventoryLocationId] -= $lineItem->qty; } - $movements->push(new InventoryCommittedMovement([ - 'inventoryItem' => $level->getInventoryItem(), - 'fromInventoryLocation' => $level->getInventoryLocation(), - 'toInventoryLocation' => $level->getInventoryLocation(), - 'fromInventoryTransactionType' => InventoryTransactionType::AVAILABLE, - 'toInventoryTransactionType' => InventoryTransactionType::COMMITTED, - 'quantity' => $lineItem->qty, - 'lineItemId' => $lineItem->id, - ])); + $inventoryCommittedMovement = new InventoryCommittedMovement(); + $inventoryCommittedMovement->inventoryItemId = $level->inventoryItemId; + $inventoryCommittedMovement->fromInventoryLocation = $level->getInventoryLocation(); + $inventoryCommittedMovement->toInventoryLocation = $level->getInventoryLocation(); + $inventoryCommittedMovement->fromInventoryTransactionType = InventoryTransactionType::AVAILABLE; + $inventoryCommittedMovement->toInventoryTransactionType = InventoryTransactionType::COMMITTED; + $inventoryCommittedMovement->quantity = $lineItem->qty; + $inventoryCommittedMovement->lineItemId = $lineItem->id; + + $movements->push($inventoryCommittedMovement); } } @@ -718,15 +726,16 @@ public function orderCompleteHandler(Order $order) $availableTotalByPurchasableIdAndLocationId[$purchasableId . '-' . $level->inventoryLocationId] -= $qtyToReserve; - $movements->push(new InventoryManualMovement([ - 'inventoryItem' => $level->getInventoryItem(), - 'fromInventoryLocation' => $level->getInventoryLocation(), - 'toInventoryLocation' => $level->getInventoryLocation(), - 'fromInventoryTransactionType' => InventoryTransactionType::AVAILABLE, - 'toInventoryTransactionType' => InventoryTransactionType::RESERVED, - 'quantity' => $qtyToReserve, - 'lineItemId' => $lineItemId, - ])); + $inventoryManualMovement = new InventoryManualMovement(); + $inventoryManualMovement->inventoryItemId = $level->inventoryItemId; + $inventoryManualMovement->fromInventoryLocation = $level->getInventoryLocation(); + $inventoryManualMovement->toInventoryLocation = $level->getInventoryLocation(); + $inventoryManualMovement->fromInventoryTransactionType = InventoryTransactionType::AVAILABLE; + $inventoryManualMovement->toInventoryTransactionType = InventoryTransactionType::RESERVED; + $inventoryManualMovement->quantity = $qtyToReserve; + $inventoryManualMovement->lineItemId = $lineItemId; + + $movements->push($inventoryManualMovement); $qty -= $qtyToReserve; if ($qty <= 0) { diff --git a/src/services/InventoryLocations.php b/src/services/InventoryLocations.php index 31ead4e14f..c5d4375c81 100644 --- a/src/services/InventoryLocations.php +++ b/src/services/InventoryLocations.php @@ -180,7 +180,7 @@ public function executeDeactivateInventoryLocation(DeactivateInventoryLocation $ $inventoryMovement = new InventoryLocationDeactivatedMovement(); $inventoryMovement->fromInventoryLocation = $deactivateInventoryLocation->inventoryLocation; $inventoryMovement->toInventoryLocation = $deactivateInventoryLocation->destinationInventoryLocation; - $inventoryMovement->inventoryItem = $inventoryLevel->getInventoryItem(); + $inventoryMovement->inventoryItemId = $inventoryLevel->inventoryItemId; $inventoryMovement->quantity = $inventoryLevel->getTotal($type); $inventoryMovement->fromInventoryTransactionType = $type; $inventoryMovement->toInventoryTransactionType = $type; From 78e1f4b436151128ec194a3c8a2ecd690f1d600f Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Thu, 5 Dec 2024 14:51:17 +0000 Subject: [PATCH 54/69] Tidy --- src/base/InventoryMovement.php | 1 - src/models/inventory/UpdateInventoryLevel.php | 1 - src/models/inventory/UpdateInventoryLevelInTransfer.php | 2 -- 3 files changed, 4 deletions(-) diff --git a/src/base/InventoryMovement.php b/src/base/InventoryMovement.php index f858d301a1..6d8ea1b6d3 100644 --- a/src/base/InventoryMovement.php +++ b/src/base/InventoryMovement.php @@ -8,7 +8,6 @@ namespace craft\commerce\base; use craft\commerce\enums\InventoryTransactionType; -use craft\commerce\models\InventoryItem; use craft\commerce\models\InventoryLocation; /** diff --git a/src/models/inventory/UpdateInventoryLevel.php b/src/models/inventory/UpdateInventoryLevel.php index c0e3986279..38ac79cd63 100644 --- a/src/models/inventory/UpdateInventoryLevel.php +++ b/src/models/inventory/UpdateInventoryLevel.php @@ -7,7 +7,6 @@ use craft\commerce\base\InventoryLocationTrait; use craft\commerce\enums\InventoryTransactionType; use craft\commerce\enums\InventoryUpdateQuantityType; -use craft\commerce\models\InventoryItem; /** * Update (Set and Adjust) Inventory Quantity model diff --git a/src/models/inventory/UpdateInventoryLevelInTransfer.php b/src/models/inventory/UpdateInventoryLevelInTransfer.php index 553e40eb17..e85aee3cd9 100644 --- a/src/models/inventory/UpdateInventoryLevelInTransfer.php +++ b/src/models/inventory/UpdateInventoryLevelInTransfer.php @@ -7,8 +7,6 @@ use craft\commerce\base\InventoryLocationTrait; use craft\commerce\enums\InventoryTransactionType; use craft\commerce\enums\InventoryUpdateQuantityType; -use craft\commerce\models\InventoryItem; -use craft\commerce\models\InventoryLocation; /** * Update (Set and Adjust) Inventory Quantity model From f5ebebf2b901d5902d64e95d57a139f84bc2c100 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Thu, 5 Dec 2024 14:51:31 +0000 Subject: [PATCH 55/69] Add `Purchasable::setStockLevel()` --- CHANGELOG-WIP.md | 9 +- src/base/Purchasable.php | 82 +++++----------- .../elements/variant/VariantStockTest.php | 93 +++++++++++++++++++ 3 files changed, 126 insertions(+), 58 deletions(-) create mode 100644 tests/unit/elements/variant/VariantStockTest.php diff --git a/CHANGELOG-WIP.md b/CHANGELOG-WIP.md index c9e510bf7d..cd6c262b92 100644 --- a/CHANGELOG-WIP.md +++ b/CHANGELOG-WIP.md @@ -1,7 +1,14 @@ # Release Notes for Craft Commerce (WIP) -## Administration +### Administration - Added support for `to`, `bcc`, and `cc` email fields to support environment variables. ([#3738](https://github.com/craftcms/commerce/issues/3738)) - Added an `originalCart` value to the `commerce/update-cart` failed ajax response. ([#430](https://github.com/craftcms/commerce/issues/430)) - Added a new "Payment Gateway" order condition rule. ([#3722](https://github.com/craftcms/commerce/discussions/3722)) + + +### System + +- Added `craft\commerce\base\InventoryItemTrait`. +- Added `craft\commerce\base\InventoryLocationTrait`. +- Added `craft\commerce\base\Purchasable::setStockLevel()`. \ No newline at end of file diff --git a/src/base/Purchasable.php b/src/base/Purchasable.php index 1701e4981e..3c88c51113 100644 --- a/src/base/Purchasable.php +++ b/src/base/Purchasable.php @@ -13,6 +13,8 @@ use craft\commerce\collections\UpdateInventoryLevelCollection; use craft\commerce\db\Table; use craft\commerce\elements\Order; +use craft\commerce\enums\InventoryTransactionType; +use craft\commerce\enums\InventoryUpdateQuantityType; use craft\commerce\errors\StoreNotFoundException; use craft\commerce\helpers\Currency; use craft\commerce\helpers\Localization; @@ -44,6 +46,7 @@ use Money\Money; use Money\Teller; use yii\base\InvalidConfigException; +use yii\db\Exception; use yii\validators\Validator; /** @@ -952,67 +955,32 @@ public function getStock(): int } /** + * @param int $quantity + * @param array $updateAttributes * @return void + * @throws InvalidConfigException + * @throws Exception * @since 5.3.0 */ - public function setStockLevel($amount): void - { - - // $commercePlugin = Plugin::getInstance(); - // - // // For simplicity let's just get the first inventory location - // $inventoryLocation = $commercePlugin->getInventoryLocations()->getAllInventoryLocations()->first(); - // - // // Retrieve the inventory item ID for the variants you want to update - // $variantInventoryItemQuery = \craft\commerce\elements\Variant::find() - // // ->select(['inventoryitems.id as inventoryItemId']); - // ->select(['inventoryitems.id as inventoryItemId']) - // ->andWhere(['elements.id' => 327]); - // // Can add extra criteria to the query if needed - // - // // Retrieve the inventory items for the variants - // $inventoryItems = $commercePlugin->getInventory()->getInventoryItemQuery() - // ->andWhere(['id' => $variantInventoryItemQuery]) - // ->collect() - // ->map(function($inventoryItem) { - // return new InventoryItem($inventoryItem); - // }); - // - // // Retrieve the inventory levels for the inventory items - // $inventoryLevels = $commercePlugin->getInventory()->getInventoryLevelQuery() - // ->andWhere(['ii.id' => $variantInventoryItemQuery]) - // ->andWhere(['it.inventoryLocationId' => $inventoryLocation->id]) - // ->collect() - // ->map(function($inventoryLevel) { - // unset($inventoryLevel['purchasableId']); - // return new InventoryLevel($inventoryLevel); - // }); - // - // $updateInventoryLevels = UpdateInventoryLevelCollection::make(); - // - // $inventoryLevels->each(function(InventoryLevel $inventoryLevel) use ($updateInventoryLevels, $inventoryItems, $inventoryLocation) { - // // Update action can be `SET` or `ADJUST` based on inserting or updating the stock - // $updateAction = \craft\commerce\enums\InventoryUpdateQuantityType::SET; - // - // // Stock amount, this would need to be based on your requirements/data being imported - // $stock = 1; // stock amount - // - // $updateInventoryLevels->push(new UpdateInventoryLevel([ - // 'type' => InventoryTransactionType::AVAILABLE->value, - // 'updateAction' => $updateAction, - // 'inventoryItem' => $inventoryItems->firstWhere('id', $inventoryLevel->inventoryItemId), - // 'inventoryLocation' => $inventoryLocation, - // 'quantity' => $stock, - // 'note' => '', - // ]) - // ); - // }); - // - // if ($updateInventoryLevels->count() > 0) { - // $commercePlugin->getInventory()->executeUpdateInventoryLevels($updateInventoryLevels); - // } - } + public function setStockLevel(int $quantity, array $updateAttributes = []): void + { + $updateAttributes += [ + 'quantity' => $quantity, + 'updateAction' => InventoryUpdateQuantityType::SET, + 'inventoryLocationId' => $this->getStore()->getInventoryLocations()->first()->id, + 'type' => InventoryTransactionType::AVAILABLE->value, + ]; + $updateInventoryLevel = new UpdateInventoryLevel($updateAttributes); + $updateInventoryLevel->inventoryItemId = $this->inventoryItemId; + + $updateInventoryLevels = UpdateInventoryLevelCollection::make(); + $updateInventoryLevels->push($updateInventoryLevel); + + Plugin::getInstance()->getInventory()->executeUpdateInventoryLevels($updateInventoryLevels); + + $this->_stock = null; + } /** * Returns the total stock across all locations this purchasable is tracked in. diff --git a/tests/unit/elements/variant/VariantStockTest.php b/tests/unit/elements/variant/VariantStockTest.php new file mode 100644 index 0000000000..de710590d7 --- /dev/null +++ b/tests/unit/elements/variant/VariantStockTest.php @@ -0,0 +1,93 @@ + + * @since 5.3.0 + */ +class VariantStockTest extends Unit +{ + /** + * @return array + */ + public function _fixtures(): array + { + return [ + 'products' => [ + 'class' => ProductFixture::class, + ], + ]; + } + + /** + * @param array $updateConfigs + * @return void + * @throws \yii\base\InvalidConfigException + * @throws \yii\db\Exception + * @dataProvider setStockLevelDataProvider + */ + public function testSetStockLevel(array $updateConfigs, int $expected): void + { + $variant = Variant::find()->sku('rad-hood')->one(); + $originalStock = $variant->getStock(); + + foreach ($updateConfigs as $updateConfig) { + $qty = $updateConfig['quantity']; + unset($updateConfig['quantity']); + + $variant->setStockLevel($qty, $updateConfig); + } + + self::assertEquals($expected, $variant->getStock()); + + $variant->setStockLevel($originalStock); + } + + /** + * @return array[] + */ + public function setStockLevelDataProvider(): array + { + return [ + 'simple-single-arg' => [ + [ + ['quantity' => 10], + ], + 'expected' => 10, + ], + 'set-and-adjust' => [ + [ + ['quantity' => 10], + ['quantity' => 2, 'updateAction' => InventoryUpdateQuantityType::ADJUST], + ], + 'expected' => 12, + ], + 'just-adjust' => [ + [ + ['quantity' => 2, 'updateAction' => InventoryUpdateQuantityType::ADJUST], + ], + 'expected' => 2, + ], + 'set-and-adjust-negative' => [ + [ + ['quantity' => 10], + ['quantity' => -2, 'updateAction' => InventoryUpdateQuantityType::ADJUST], + ], + 'expected' => 8, + ], + ]; + } +} From f9b4fae277444862a8f0a3320b5835ad890f0005 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Thu, 5 Dec 2024 15:06:42 +0000 Subject: [PATCH 56/69] Added `Iventory::updateInventoryLevel()` --- CHANGELOG-WIP.md | 3 ++- src/base/Purchasable.php | 19 +++---------------- src/services/Inventory.php | 26 ++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/CHANGELOG-WIP.md b/CHANGELOG-WIP.md index cd6c262b92..90f6838d32 100644 --- a/CHANGELOG-WIP.md +++ b/CHANGELOG-WIP.md @@ -11,4 +11,5 @@ - Added `craft\commerce\base\InventoryItemTrait`. - Added `craft\commerce\base\InventoryLocationTrait`. -- Added `craft\commerce\base\Purchasable::setStockLevel()`. \ No newline at end of file +- Added `craft\commerce\base\Purchasable::setStockLevel()`. +- Added `craft\commerce\services\Inventory::updateInventoryLevel()`. \ No newline at end of file diff --git a/src/base/Purchasable.php b/src/base/Purchasable.php index 3c88c51113..2c094b4c77 100644 --- a/src/base/Purchasable.php +++ b/src/base/Purchasable.php @@ -956,28 +956,15 @@ public function getStock(): int /** * @param int $quantity - * @param array $updateAttributes + * @param array $updateInventoryLevelAttributes * @return void * @throws InvalidConfigException * @throws Exception * @since 5.3.0 */ - public function setStockLevel(int $quantity, array $updateAttributes = []): void + public function setStockLevel(int $quantity, array $updateInventoryLevelAttributes = []): void { - $updateAttributes += [ - 'quantity' => $quantity, - 'updateAction' => InventoryUpdateQuantityType::SET, - 'inventoryLocationId' => $this->getStore()->getInventoryLocations()->first()->id, - 'type' => InventoryTransactionType::AVAILABLE->value, - ]; - - $updateInventoryLevel = new UpdateInventoryLevel($updateAttributes); - $updateInventoryLevel->inventoryItemId = $this->inventoryItemId; - - $updateInventoryLevels = UpdateInventoryLevelCollection::make(); - $updateInventoryLevels->push($updateInventoryLevel); - - Plugin::getInstance()->getInventory()->executeUpdateInventoryLevels($updateInventoryLevels); + Plugin::getInstance()->getInventory()->updateInventoryLevel($this->inventoryItemId, $quantity, $updateInventoryLevelAttributes); $this->_stock = null; } diff --git a/src/services/Inventory.php b/src/services/Inventory.php index a3324876a0..0dc088b619 100644 --- a/src/services/Inventory.php +++ b/src/services/Inventory.php @@ -325,6 +325,32 @@ public function executeUpdateInventoryLevels(UpdateInventoryLevelCollection $upd } } + /** + * @param int $inventoryItemId + * @param int $quantity + * @param array $updateInventoryLevelAttributes + * @return void + * @throws Exception + * @throws InvalidConfigException + */ + public function updateInventoryLevel(int $inventoryItemId, int $quantity, array $updateInventoryLevelAttributes = []) + { + $updateInventoryLevelAttributes += [ + 'quantity' => $quantity, + 'updateAction' => InventoryUpdateQuantityType::SET, + 'inventoryLocationId' => $this->getStore()->getInventoryLocations()->first()->id, + 'type' => InventoryTransactionType::AVAILABLE->value, + ]; + + $updateInventoryLevel = new UpdateInventoryLevel($updateInventoryLevelAttributes); + $updateInventoryLevel->inventoryItemId = $inventoryItemId; + + $updateInventoryLevels = UpdateInventoryLevelCollection::make(); + $updateInventoryLevels->push($updateInventoryLevel); + + Plugin::getInstance()->getInventory()->executeUpdateInventoryLevels($updateInventoryLevels); + } + /** * @param UpdateInventoryLevel|UpdateInventoryLevelInTransfer $updateInventoryLevel * @return bool From 026b521f0cd83cb8303defbabb089d12fcf22237 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Thu, 5 Dec 2024 15:15:35 +0000 Subject: [PATCH 57/69] fix cs --- src/base/Purchasable.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/base/Purchasable.php b/src/base/Purchasable.php index 2c094b4c77..40ecab5f13 100644 --- a/src/base/Purchasable.php +++ b/src/base/Purchasable.php @@ -10,16 +10,12 @@ use Craft; use craft\base\Element; use craft\base\NestedElementInterface; -use craft\commerce\collections\UpdateInventoryLevelCollection; use craft\commerce\db\Table; use craft\commerce\elements\Order; -use craft\commerce\enums\InventoryTransactionType; -use craft\commerce\enums\InventoryUpdateQuantityType; use craft\commerce\errors\StoreNotFoundException; use craft\commerce\helpers\Currency; use craft\commerce\helpers\Localization; use craft\commerce\helpers\Purchasable as PurchasableHelper; -use craft\commerce\models\inventory\UpdateInventoryLevel; use craft\commerce\models\InventoryItem; use craft\commerce\models\InventoryLevel; use craft\commerce\models\LineItem; From 4f007faae0ae3475bd6ddfdbc8fb49b509195e46 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Thu, 5 Dec 2024 15:17:45 +0000 Subject: [PATCH 58/69] fix default location --- src/base/Purchasable.php | 4 ++++ src/services/Inventory.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/base/Purchasable.php b/src/base/Purchasable.php index 40ecab5f13..ae4e204e84 100644 --- a/src/base/Purchasable.php +++ b/src/base/Purchasable.php @@ -960,6 +960,10 @@ public function getStock(): int */ public function setStockLevel(int $quantity, array $updateInventoryLevelAttributes = []): void { + $updateInventoryLevelAttributes += [ + 'inventoryLocationId' => $this->getStore()->getInventoryLocations()->first()->id, + ]; + Plugin::getInstance()->getInventory()->updateInventoryLevel($this->inventoryItemId, $quantity, $updateInventoryLevelAttributes); $this->_stock = null; diff --git a/src/services/Inventory.php b/src/services/Inventory.php index 0dc088b619..a1f2ae4769 100644 --- a/src/services/Inventory.php +++ b/src/services/Inventory.php @@ -338,7 +338,7 @@ public function updateInventoryLevel(int $inventoryItemId, int $quantity, array $updateInventoryLevelAttributes += [ 'quantity' => $quantity, 'updateAction' => InventoryUpdateQuantityType::SET, - 'inventoryLocationId' => $this->getStore()->getInventoryLocations()->first()->id, + 'inventoryLocationId' => Plugin::getInstance()->getInventoryLocations()->getAllInventoryLocations()->first()->id, 'type' => InventoryTransactionType::AVAILABLE->value, ]; From 99bb3f8400c473e5dc9ae543cba631a4a096e156 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Thu, 5 Dec 2024 15:38:26 +0000 Subject: [PATCH 59/69] Add exception to stop being able to set stock level on an unsaved purchasable --- src/base/Purchasable.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/base/Purchasable.php b/src/base/Purchasable.php index ae4e204e84..af58288e1b 100644 --- a/src/base/Purchasable.php +++ b/src/base/Purchasable.php @@ -960,6 +960,10 @@ public function getStock(): int */ public function setStockLevel(int $quantity, array $updateInventoryLevelAttributes = []): void { + if ($this->inventoryItemId === null) { + throw new InvalidConfigException('Cannot set stock level on a purchasable without an inventory item.'); + } + $updateInventoryLevelAttributes += [ 'inventoryLocationId' => $this->getStore()->getInventoryLocations()->first()->id, ]; From 134d83c5c5d843246d4983c8a0af02d2e526d1c7 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Thu, 5 Dec 2024 15:42:13 +0000 Subject: [PATCH 60/69] Tidy [ci skip] --- src/elements/Transfer.php | 68 +++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/src/elements/Transfer.php b/src/elements/Transfer.php index e13ed7d21e..b6da0accef 100644 --- a/src/elements/Transfer.php +++ b/src/elements/Transfer.php @@ -191,7 +191,7 @@ public function setTransferStatus(TransferStatusType|string $status): void } /** - * @inheritDoc + * @inheritdoc */ public static function displayName(): string { @@ -199,7 +199,7 @@ public static function displayName(): string } /** - * @inheritDoc + * @inheritdoc */ public static function lowerDisplayName(): string { @@ -207,7 +207,7 @@ public static function lowerDisplayName(): string } /** - * @inheritDoc + * @inheritdoc */ public static function pluralDisplayName(): string { @@ -215,7 +215,7 @@ public static function pluralDisplayName(): string } /** - * @inheritDoc + * @inheritdoc */ public static function pluralLowerDisplayName(): string { @@ -223,7 +223,7 @@ public static function pluralLowerDisplayName(): string } /** - * @inheritDoc + * @inheritdoc */ public static function refHandle(): ?string { @@ -231,7 +231,7 @@ public static function refHandle(): ?string } /** - * @inheritDoc + * @inheritdoc */ public static function trackChanges(): bool { @@ -239,7 +239,7 @@ public static function trackChanges(): bool } /** - * @inheritDoc + * @inheritdoc */ public static function hasTitles(): bool { @@ -247,7 +247,7 @@ public static function hasTitles(): bool } /** - * @inheritDoc + * @inheritdoc */ public static function hasContent(): bool { @@ -255,7 +255,7 @@ public static function hasContent(): bool } /** - * @inheritDoc + * @inheritdoc */ public static function hasUris(): bool { @@ -263,7 +263,7 @@ public static function hasUris(): bool } /** - * @inheritDoc + * @inheritdoc */ public static function isLocalized(): bool { @@ -271,7 +271,7 @@ public static function isLocalized(): bool } /** - * @inheritDoc + * @inheritdoc */ public static function hasStatuses(): bool { @@ -280,7 +280,7 @@ public static function hasStatuses(): bool /** * @return TransferQuery - * @inheritDoc + * @inheritdoc */ public static function find(): ElementQueryInterface { @@ -288,7 +288,7 @@ public static function find(): ElementQueryInterface } /** - * @inheritDoc + * @inheritdoc */ public static function createCondition(): ElementConditionInterface { @@ -296,7 +296,7 @@ public static function createCondition(): ElementConditionInterface } /** - * @inheritDoc + * @inheritdoc */ protected static function includeSetStatusAction(): bool { @@ -331,7 +331,7 @@ protected static function defineSortOptions(): array } /** - * @inheritDoc + * @inheritdoc */ protected static function defineTableAttributes(): array { @@ -347,7 +347,7 @@ protected static function defineTableAttributes(): array } /** - * @inheritDoc + * @inheritdoc */ protected static function defineDefaultTableAttributes(string $source): array { @@ -359,7 +359,7 @@ protected static function defineDefaultTableAttributes(string $source): array } /** - * @inheritDoc + * @inheritdoc */ protected function attributeHtml(string $attribute): string { @@ -384,7 +384,7 @@ protected function attributeHtml(string $attribute): string } /** - * @inheritDoc + * @inheritdoc */ protected function defineRules(): array { @@ -436,7 +436,7 @@ public function validateLocations($attribute, $params, $validator) } /** - * @inheritDoc + * @inheritdoc */ public function getUriFormat(): ?string { @@ -480,7 +480,7 @@ protected static function defineSources(string $context = null): array /** * - * @inheritDoc + * @inheritdoc */ protected function previewTargets(): array { @@ -497,6 +497,10 @@ protected function previewTargets(): array return $previewTargets; } + + /** + * @inheritdoc + */ protected function safeActionMenuItems(): array { $safeActions = parent::safeActionMenuItems(); @@ -518,7 +522,7 @@ protected function safeActionMenuItems(): array /** - * @inheritDoc + * @inheritdoc */ protected function route(): array|string|null { @@ -533,7 +537,7 @@ protected function route(): array|string|null } /** - * @inheritDoc + * @inheritdoc */ public function canView(User $user): bool { @@ -545,7 +549,7 @@ public function canView(User $user): bool } /** - * @inheritDoc + * @inheritdoc */ public function canSave(User $user): bool { @@ -557,7 +561,7 @@ public function canSave(User $user): bool } /** - * @inheritDoc + * @inheritdoc */ public function canDuplicate(User $user): bool { @@ -565,7 +569,7 @@ public function canDuplicate(User $user): bool } /** - * @inheritDoc + * @inheritdoc */ public function canDelete(User $user): bool { @@ -583,7 +587,7 @@ public function canDelete(User $user): bool } /** - * @inheritDoc + * @inheritdoc */ public function canCreateDrafts(User $user): bool { @@ -591,7 +595,7 @@ public function canCreateDrafts(User $user): bool } /** - * @inheritDoc + * @inheritdoc */ protected function cpEditUrl(): ?string { @@ -599,7 +603,7 @@ protected function cpEditUrl(): ?string } /** - * @inheritDoc + * @inheritdoc */ public function getPostEditUrl(): ?string { @@ -607,7 +611,7 @@ public function getPostEditUrl(): ?string } /** - * @inheritDoc + * @inheritdoc */ public function prepareEditScreen(Response $response, string $containerId): void { @@ -735,7 +739,7 @@ public function addDetail(TransferDetail $detail): void } /** - * @inheritDoc + * @inheritdoc */ public function getFieldLayout(): ?FieldLayout { @@ -743,7 +747,7 @@ public function getFieldLayout(): ?FieldLayout } /** - * @inheritDoc + * @inheritdoc */ public function beforeValidate() { @@ -755,7 +759,7 @@ public function beforeValidate() } /** - * @inheritDoc + * @inheritdoc */ public function afterSave(bool $isNew): void { From 4ffa9b671f7a72a742208384c16ccf3e33c56f0d Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Fri, 6 Dec 2024 09:47:59 +0000 Subject: [PATCH 61/69] Switch to just using service method for updating purchasable inventory level --- CHANGELOG-WIP.md | 4 +- src/base/Purchasable.php | 37 +++++++------------ src/elements/Transfer.php | 8 +++- src/services/Inventory.php | 27 ++++++++++++++ .../InventoryTest.php} | 22 +++++++---- 5 files changed, 63 insertions(+), 35 deletions(-) rename tests/unit/{elements/variant/VariantStockTest.php => services/InventoryTest.php} (75%) diff --git a/CHANGELOG-WIP.md b/CHANGELOG-WIP.md index 90f6838d32..39fcf07f8c 100644 --- a/CHANGELOG-WIP.md +++ b/CHANGELOG-WIP.md @@ -11,5 +11,5 @@ - Added `craft\commerce\base\InventoryItemTrait`. - Added `craft\commerce\base\InventoryLocationTrait`. -- Added `craft\commerce\base\Purchasable::setStockLevel()`. -- Added `craft\commerce\services\Inventory::updateInventoryLevel()`. \ No newline at end of file +- Added `craft\commerce\services\Inventory::updateInventoryLevel()`. +- Added `craft\commerce\services\Inventory::updatePurchasableInventoryLevel()`. \ No newline at end of file diff --git a/src/base/Purchasable.php b/src/base/Purchasable.php index af58288e1b..a30ad96584 100644 --- a/src/base/Purchasable.php +++ b/src/base/Purchasable.php @@ -384,6 +384,20 @@ private function _getTeller(): Teller return Plugin::getInstance()->getCurrencies()->getTeller($this->getStore()->getCurrency()); } + /** + * @inheritdoc + */ + public function __unset($name) + { + // Allow clearing of specific memoized properties + if (in_array($name, ['stock', 'shippingCategory', 'taxCategory'])) { + $this->{'_' . $name} = null; + return; + } + + parent::__unset($name); + } + /** * @inheritdoc */ @@ -950,29 +964,6 @@ public function getStock(): int return $this->_stock; } - /** - * @param int $quantity - * @param array $updateInventoryLevelAttributes - * @return void - * @throws InvalidConfigException - * @throws Exception - * @since 5.3.0 - */ - public function setStockLevel(int $quantity, array $updateInventoryLevelAttributes = []): void - { - if ($this->inventoryItemId === null) { - throw new InvalidConfigException('Cannot set stock level on a purchasable without an inventory item.'); - } - - $updateInventoryLevelAttributes += [ - 'inventoryLocationId' => $this->getStore()->getInventoryLocations()->first()->id, - ]; - - Plugin::getInstance()->getInventory()->updateInventoryLevel($this->inventoryItemId, $quantity, $updateInventoryLevelAttributes); - - $this->_stock = null; - } - /** * Returns the total stock across all locations this purchasable is tracked in. * @return Collection diff --git a/src/elements/Transfer.php b/src/elements/Transfer.php index b6da0accef..df1b16120e 100644 --- a/src/elements/Transfer.php +++ b/src/elements/Transfer.php @@ -82,11 +82,17 @@ public function __toString(): string ]); } + /** + * @inheritdoc + */ public static function hasDrafts(): bool { return false; } + /** + * @inheritdoc + */ protected function metadata(): array { $additionalMeta = []; @@ -497,7 +503,6 @@ protected function previewTargets(): array return $previewTargets; } - /** * @inheritdoc */ @@ -520,7 +525,6 @@ protected function safeActionMenuItems(): array return $safeActions; } - /** * @inheritdoc */ diff --git a/src/services/Inventory.php b/src/services/Inventory.php index a1f2ae4769..9150a2f392 100644 --- a/src/services/Inventory.php +++ b/src/services/Inventory.php @@ -332,6 +332,7 @@ public function executeUpdateInventoryLevels(UpdateInventoryLevelCollection $upd * @return void * @throws Exception * @throws InvalidConfigException + * @since 5.3.0 */ public function updateInventoryLevel(int $inventoryItemId, int $quantity, array $updateInventoryLevelAttributes = []) { @@ -351,6 +352,32 @@ public function updateInventoryLevel(int $inventoryItemId, int $quantity, array Plugin::getInstance()->getInventory()->executeUpdateInventoryLevels($updateInventoryLevels); } + /** + * @param Purchasable $purchasable + * @param int $quantity + * @param array $updateInventoryLevelAttributes + * @return void + * @throws Exception + * @throws InvalidConfigException + * @throws \craft\errors\DeprecationException + * @since 5.3.0 + */ + public function updatePurchasableInventoryLevel(Purchasable $purchasable, int $quantity, array $updateInventoryLevelAttributes = []) + { + $updateInventoryLevelAttributes += [ + 'quantity' => $quantity, + 'updateAction' => InventoryUpdateQuantityType::SET, + 'inventoryItemId' => $purchasable->inventoryItemId, + 'inventoryLocationId' => $purchasable->getStore()->getInventoryLocations()->first()->id, + 'type' => InventoryTransactionType::AVAILABLE->value, + ]; + + $this->updateInventoryLevel($purchasable->inventoryItemId, $quantity, $updateInventoryLevelAttributes); + + // Clear the stock cache for the class instance + unset($purchasable->stock); + } + /** * @param UpdateInventoryLevel|UpdateInventoryLevelInTransfer $updateInventoryLevel * @return bool diff --git a/tests/unit/elements/variant/VariantStockTest.php b/tests/unit/services/InventoryTest.php similarity index 75% rename from tests/unit/elements/variant/VariantStockTest.php rename to tests/unit/services/InventoryTest.php index de710590d7..78ae95fb76 100644 --- a/tests/unit/elements/variant/VariantStockTest.php +++ b/tests/unit/services/InventoryTest.php @@ -5,20 +5,24 @@ * @license https://craftcms.github.io/license/ */ -namespace unit\elements\variant; +namespace craftcommercetests\unit\services; use Codeception\Test\Unit; use craft\commerce\elements\Variant; use craft\commerce\enums\InventoryUpdateQuantityType; +use craft\commerce\Plugin; +use craft\errors\DeprecationException; use craftcommercetests\fixtures\ProductFixture; +use yii\base\InvalidConfigException; +use yii\db\Exception; /** - * VariantStockTest + * InventoryTest * * @author Pixel & Tonic, Inc. * @since 5.3.0 */ -class VariantStockTest extends Unit +class InventoryTest extends Unit { /** * @return array @@ -34,12 +38,14 @@ public function _fixtures(): array /** * @param array $updateConfigs + * @param int $expected * @return void - * @throws \yii\base\InvalidConfigException - * @throws \yii\db\Exception + * @throws DeprecationException + * @throws InvalidConfigException + * @throws Exception * @dataProvider setStockLevelDataProvider */ - public function testSetStockLevel(array $updateConfigs, int $expected): void + public function testUpdatePurchasableInventoryLevel(array $updateConfigs, int $expected): void { $variant = Variant::find()->sku('rad-hood')->one(); $originalStock = $variant->getStock(); @@ -48,12 +54,12 @@ public function testSetStockLevel(array $updateConfigs, int $expected): void $qty = $updateConfig['quantity']; unset($updateConfig['quantity']); - $variant->setStockLevel($qty, $updateConfig); + Plugin::getInstance()->getInventory()->updatePurchasableInventoryLevel($variant, $qty, $updateConfig); } self::assertEquals($expected, $variant->getStock()); - $variant->setStockLevel($originalStock); + Plugin::getInstance()->getInventory()->updatePurchasableInventoryLevel($variant, $originalStock); } /** From f29a51acea3d429a928a842f601d62313f3f20cd Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Fri, 6 Dec 2024 09:51:10 +0000 Subject: [PATCH 62/69] Fix cs --- src/base/Purchasable.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/base/Purchasable.php b/src/base/Purchasable.php index a30ad96584..6f7f7feee5 100644 --- a/src/base/Purchasable.php +++ b/src/base/Purchasable.php @@ -42,7 +42,6 @@ use Money\Money; use Money\Teller; use yii\base\InvalidConfigException; -use yii\db\Exception; use yii\validators\Validator; /** From 12e7920fe224ea9c720eeb246b7b5e65f70544b0 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Fri, 6 Dec 2024 16:04:27 +0000 Subject: [PATCH 63/69] Fixed errors not showing on order edit after saving --- CHANGELOG.md | 4 ++++ src/controllers/OrdersController.php | 1 + 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b86159db3..c2030227c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Release Notes for Craft Commerce +## Unreleased + +- Fixed a bug where order errors weren't showing on the Edit Order page. + ## 4.7.1 - 2024-12-02 - Fixed an error that occurred on the Orders index page when running Craft CMS 4.13.4 or later. ([#3793](https://github.com/craftcms/commerce/issues/3793)) diff --git a/src/controllers/OrdersController.php b/src/controllers/OrdersController.php index 0a7533352f..a35dfc37db 100644 --- a/src/controllers/OrdersController.php +++ b/src/controllers/OrdersController.php @@ -1251,6 +1251,7 @@ private function _registerJavascript(array $variables): void if ($variables['order']->hasErrors()) { $response['order']['errors'] = $variables['order']->getErrors(); + $response['errors'] = $variables['order']->getErrors(); $response['error'] = Craft::t('commerce', 'The order is not valid.'); } From 206b1f8ff1b8b3783f5813673973beaec180571d Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Tue, 10 Dec 2024 08:39:59 +0000 Subject: [PATCH 64/69] #3807 default price showing incorrectly for products in CP --- CHANGELOG.md | 1 + src/elements/Variant.php | 1 + src/elements/db/ProductQuery.php | 14 ++++++++------ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93168ab77d..8a8f17cd62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Fixed a bug where a product’s default price was showing incorrectly on the Products index page. ([#3807](https://github.com/craftcms/commerce/issues/3807)) - Fixed a bug where order errors weren't showing on the Edit Order page. ## 5.2.8 - 2024-12-04 diff --git a/src/elements/Variant.php b/src/elements/Variant.php index 1ab9f48363..a556e1c0b0 100755 --- a/src/elements/Variant.php +++ b/src/elements/Variant.php @@ -1068,6 +1068,7 @@ public function afterSave(bool $isNew): void parent::afterSave($isNew); if (!$this->propagating && $this->isDefault && $ownerId && $this->duplicateOf === null) { + // @TODO - this data is now joined in on the product query so can be removed at the next breaking change $defaultData = [ 'defaultVariantId' => $this->id, 'defaultSku' => $this->getSkuAsText(), diff --git a/src/elements/db/ProductQuery.php b/src/elements/db/ProductQuery.php index 28be43d670..745a542cf7 100644 --- a/src/elements/db/ProductQuery.php +++ b/src/elements/db/ProductQuery.php @@ -768,18 +768,20 @@ protected function beforePrepare(): bool 'commerce_products.postDate', 'commerce_products.expiryDate', 'subquery.price as defaultPrice', - 'commerce_products.defaultPrice as defaultBasePrice', + 'purchasablesstores.basePrice as defaultBasePrice', 'commerce_products.defaultVariantId', - 'commerce_products.defaultSku', - 'commerce_products.defaultWeight', - 'commerce_products.defaultLength', - 'commerce_products.defaultWidth', - 'commerce_products.defaultHeight', + 'purchasables.sku as defaultSku', + 'purchasables.weight as defaultWeight', + 'purchasables.length as defaultLength', + 'purchasables.width as defaultWidth', + 'purchasables.height as defaultHeight', 'sitestores.storeId', ]); // Join in sites stores to get product's store for current request $this->query->leftJoin(['sitestores' => Table::SITESTORES], '[[elements_sites.siteId]] = [[sitestores.siteId]]'); + $this->query->leftJoin(['purchasables' => Table::PURCHASABLES], '[[purchasables.id]] = [[commerce_products.defaultVariantId]]'); + $this->query->leftJoin(['purchasablesstores' => Table::PURCHASABLES_STORES], '[[purchasablesstores.purchasableId]] = [[commerce_products.defaultVariantId]] and [[sitestores.storeId]] = [[purchasablesstores.storeId]]'); $this->subQuery->addSelect(['catalogprices.price']); From cdb042611602110cd18a6216d4032fc6f65a6acb Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Tue, 10 Dec 2024 12:03:38 +0000 Subject: [PATCH 65/69] #3805 Inline-editable matrix fields not saving on variants Co-authored-by: Iwona Just --- CHANGELOG.md | 1 + src/base/Purchasable.php | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a8f17cd62..db68d6a985 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased - Fixed a bug where a product’s default price was showing incorrectly on the Products index page. ([#3807](https://github.com/craftcms/commerce/issues/3807)) +- Fixed a bug where inline-editable Matrix fields weren’t saving content on product variants. ([#3805](https://github.com/craftcms/commerce/issues/3805)) - Fixed a bug where order errors weren't showing on the Edit Order page. ## 5.2.8 - 2024-12-04 diff --git a/src/base/Purchasable.php b/src/base/Purchasable.php index ce7906982c..764fbc4eb4 100644 --- a/src/base/Purchasable.php +++ b/src/base/Purchasable.php @@ -1119,6 +1119,8 @@ public function afterSave(bool $isNew): void */ public function afterPropagate(bool $isNew): void { + parent::afterPropagate($isNew); + Plugin::getInstance()->getCatalogPricing()->createCatalogPricingJob([ 'purchasableIds' => [$this->getCanonicalId()], 'storeId' => $this->getStoreId(), From 0d7ec5ffd7ab0584a2c48c73627f13571a86b96a Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Tue, 10 Dec 2024 12:40:02 +0000 Subject: [PATCH 66/69] Move card attributes to purchasable class --- src/base/Purchasable.php | 95 ++++++++++++++++++++++++++++++++++++++++ src/elements/Variant.php | 88 +++++-------------------------------- 2 files changed, 107 insertions(+), 76 deletions(-) diff --git a/src/base/Purchasable.php b/src/base/Purchasable.php index 764fbc4eb4..2a95d0fd36 100644 --- a/src/base/Purchasable.php +++ b/src/base/Purchasable.php @@ -1276,6 +1276,24 @@ protected function attributeHtml(string $attribute): string } } + $dimensions = []; + if ($attribute === 'dimensions') { + $dimensions = array_filter([ + $this->length, + $this->width, + $this->height + ]); + } + + if ($attribute === 'priceView') { + $price = $this->basePriceAsCurrency; + if ($this->getBasePromotionalPrice() && $this->getBasePromotionalPrice() < $this->getBasePrice()) { + $price = Html::tag('del', $price, ['style' => 'opacity: .5']) . ' ' . $this->basePromotionalPriceAsCurrency; + } + + return $price; + } + return match ($attribute) { 'sku' => (string)Html::encode($this->getSkuAsText()), 'price' => $this->basePriceAsCurrency, @@ -1287,6 +1305,7 @@ protected function attributeHtml(string $attribute): string 'minQty' => (string)$this->minQty, 'maxQty' => (string)$this->maxQty, 'stock' => $stock, + 'dimensions' => !empty($dimensions) ? implode(' x ', $dimensions) . ' ' . Plugin::getInstance()->getSettings()->dimensionUnits : '', default => parent::attributeHtml($attribute), }; } @@ -1324,6 +1343,82 @@ protected static function defineDefaultTableAttributes(string $source): array ]; } + /** + * @inheritdoc + */ + public static function attributePreviewHtml(array $attribute): mixed + { + return match($attribute['value']) { + 'sku', 'priceView', 'dimensions', 'weight' => $attribute['placeholder'], + 'availableForPurchase', 'promotable' => Html::tag('span', '', [ + 'class' => 'checkbox-icon', + 'role' => 'img', + 'title' => $attribute['label'], + 'aria' => [ + 'label' => $attribute['label'], + ], + ]) . + Html::tag('span', $attribute['label'], [ + 'class' => 'checkbox-preview-label', + ]), + default => parent::attributePreviewHtml($attribute) + }; + } + + /** + * @inheritdoc + */ + protected static function defineDefaultCardAttributes(): array + { + return array_merge(parent::defineDefaultCardAttributes(), [ + 'sku', + 'priceView', + ]); + } + + /** + * @inheritdoc + */ + protected static function defineCardAttributes(): array + { + return array_merge(Element::defineCardAttributes(), [ + 'availableForPurchase' => [ + 'label' => Craft::t('commerce', 'Available for purchase'), + ], + 'basePrice' => [ + 'label' => Craft::t('commerce', 'Base Price'), + 'placeholder' => '¤' . Craft::$app->getFormattingLocale()->getFormatter()->asDecimal(123.99), + ], + 'basePromotionalPrice' => [ + 'label' => Craft::t('commerce', 'Base Promotional Price'), + 'placeholder' => '¤' . Craft::$app->getFormattingLocale()->getFormatter()->asDecimal(123.99), + ], + 'dimensions' => [ + 'label' => Craft::t('commerce', 'Dimensions'), + 'placeholder' => '1 x 2 x 3 ' . Plugin::getInstance()->getSettings()->dimensionUnits, + ], + 'priceView' => [ + 'label' => Craft::t('commerce', 'Price'), + 'placeholder' => Html::tag('del', '¤' . Craft::$app->getFormattingLocale()->getFormatter()->asDecimal(199.99), ['style' => 'opacity: .5']) . ' ¤' . Craft::$app->getFormattingLocale()->getFormatter()->asDecimal(123.99), + ], + 'promotable' => [ + 'label' => Craft::t('commerce', 'Promotable'), + ], + 'sku' => [ + 'label' => Craft::t('commerce', 'SKU'), + 'placeholder' => Html::tag('code', 'SKU123'), + ], + 'stock' => [ + 'label' => Craft::t('commerce', 'Stock'), + 'placeholder' => 10, + ], + 'weight' => [ + 'label' => Craft::t('commerce', 'Weight'), + 'placeholder' => 123 . Plugin::getInstance()->getSettings()->weightUnits, + ] + ]); + } + /** * @inheritdoc */ diff --git a/src/elements/Variant.php b/src/elements/Variant.php index 6204d5307d..7ef4f42117 100755 --- a/src/elements/Variant.php +++ b/src/elements/Variant.php @@ -1338,73 +1338,6 @@ protected static function defineActions(string $source): array ]]; } - /** - * @inheritdoc - */ - public static function attributePreviewHtml(array $attribute): mixed - { - return match($attribute['value']) { - 'sku', 'priceView' => $attribute['placeholder'], - 'availableForPurchase', 'promotable' => Html::tag('span', '', [ - 'class' => 'checkbox-icon', - 'role' => 'img', - 'title' => $attribute['label'], - 'aria' => [ - 'label' => $attribute['label'], - ], - ]) . - Html::tag('span', $attribute['label'], [ - 'class' => 'checkbox-preview-label', - ]), - default => parent::attributePreviewHtml($attribute) - }; - } - - /** - * @inheritdoc - */ - protected static function defineCardAttributes(): array - { - return array_merge(parent::defineCardAttributes(), [ - 'basePromotionalPrice' => [ - 'label' => Craft::t('commerce', 'Base Promotional Price'), - 'placeholder' => '¤' . Craft::$app->getFormattingLocale()->getFormatter()->asDecimal(123.99), - ], - 'basePrice' => [ - 'label' => Craft::t('commerce', 'Base Price'), - 'placeholder' => '¤' . Craft::$app->getFormattingLocale()->getFormatter()->asDecimal(123.99), - ], - 'product' => [ - 'label' => Craft::t('commerce', 'Product'), - ], - 'promotable' => [ - 'label' => Craft::t('commerce', 'Promotable'), - ], - 'availableForPurchase' => [ - 'label' => Craft::t('commerce', 'Available for purchase'), - ], - 'priceView' => [ - 'label' => Craft::t('commerce', 'Price'), - 'placeholder' => Html::tag('del','¤' . Craft::$app->getFormattingLocale()->getFormatter()->asDecimal(199.99), ['style' => 'opacity: .5']) . ' ¤' . Craft::$app->getFormattingLocale()->getFormatter()->asDecimal(123.99), - ], - 'sku' => [ - 'label' => Craft::t('commerce', 'SKU'), - 'placeholder' => Html::tag('code', 'SKU123'), - ], - ]); - } - - /** - * @inheritdoc - */ - protected static function defineDefaultCardAttributes(): array - { - return array_merge(parent::defineDefaultCardAttributes(), [ - 'sku', - 'priceView', - ]); - } - /** * @inheritdoc */ @@ -1439,6 +1372,18 @@ protected static function defineSearchableAttributes(): array return [...parent::defineSearchableAttributes(), ...['productTitle']]; } + /** + * @inheritdoc + */ + protected static function defineCardAttributes(): array + { + return array_merge(parent::defineCardAttributes(), [ + 'product' => [ + 'label' => Craft::t('commerce', 'Product'), + ], + ]); + } + /** * @inheritdoc */ @@ -1453,15 +1398,6 @@ protected function attributeHtml(string $attribute): string return sprintf(' %s', $product->getStatus(), Html::encode($product->title)); } - if ($attribute === 'priceView') { - $price = $this->basePriceAsCurrency; - if ($this->getBasePromotionalPrice() && $this->getBasePromotionalPrice() < $this->getBasePrice()) { - $price = Html::tag('del', $price, ['style' => 'opacity: .5']) . ' ' . $this->basePromotionalPriceAsCurrency; - } - - return $price; - } - return parent::attributeHtml($attribute); } } From ae5601c4658de09d1b10d12c38421714b13110b9 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Tue, 10 Dec 2024 12:43:02 +0000 Subject: [PATCH 67/69] fix cs --- src/base/Purchasable.php | 8 ++++---- src/elements/Product.php | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/base/Purchasable.php b/src/base/Purchasable.php index 2a95d0fd36..d3fc8d55c3 100644 --- a/src/base/Purchasable.php +++ b/src/base/Purchasable.php @@ -1281,7 +1281,7 @@ protected function attributeHtml(string $attribute): string $dimensions = array_filter([ $this->length, $this->width, - $this->height + $this->height, ]); } @@ -1348,9 +1348,9 @@ protected static function defineDefaultTableAttributes(string $source): array */ public static function attributePreviewHtml(array $attribute): mixed { - return match($attribute['value']) { + return match ($attribute['value']) { 'sku', 'priceView', 'dimensions', 'weight' => $attribute['placeholder'], - 'availableForPurchase', 'promotable' => Html::tag('span', '', [ + 'availableForPurchase', 'promotable' => Html::tag('span', '', [ 'class' => 'checkbox-icon', 'role' => 'img', 'title' => $attribute['label'], @@ -1415,7 +1415,7 @@ protected static function defineCardAttributes(): array 'weight' => [ 'label' => Craft::t('commerce', 'Weight'), 'placeholder' => 123 . Plugin::getInstance()->getSettings()->weightUnits, - ] + ], ]); } diff --git a/src/elements/Product.php b/src/elements/Product.php index 895920d15a..8261a18620 100644 --- a/src/elements/Product.php +++ b/src/elements/Product.php @@ -570,7 +570,7 @@ protected static function defineDefaultTableAttributes(string $source): array */ public static function attributePreviewHtml(array $attribute): mixed { - return match($attribute['value']) { + return match ($attribute['value']) { 'defaultSku' => $attribute['placeholder'], default => parent::attributePreviewHtml($attribute) }; From 225c92425027b8e5621c4cc234e00bdf6f1556d7 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Wed, 11 Dec 2024 08:16:58 +0000 Subject: [PATCH 68/69] Added product condition rule for variant conditions --- CHANGELOG-WIP.md | 9 ++- .../variants/ProductConditionRule.php | 68 +++++++++++++++++++ .../conditions/variants/VariantCondition.php | 1 + 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 src/elements/conditions/variants/ProductConditionRule.php diff --git a/CHANGELOG-WIP.md b/CHANGELOG-WIP.md index c9e510bf7d..055774d780 100644 --- a/CHANGELOG-WIP.md +++ b/CHANGELOG-WIP.md @@ -1,7 +1,12 @@ # Release Notes for Craft Commerce (WIP) -## Administration +### Store Management - Added support for `to`, `bcc`, and `cc` email fields to support environment variables. ([#3738](https://github.com/craftcms/commerce/issues/3738)) - Added an `originalCart` value to the `commerce/update-cart` failed ajax response. ([#430](https://github.com/craftcms/commerce/issues/430)) -- Added a new "Payment Gateway" order condition rule. ([#3722](https://github.com/craftcms/commerce/discussions/3722)) +- Order conditions can now have a “Payment Gateway” rule. ([#3722](https://github.com/craftcms/commerce/discussions/3722)) +- Variant conditions can now have a “Product” rule. + +### Extensibility + +- Added `craft\commerce\elements\conditions\variants\ProductConditionRule`. \ No newline at end of file diff --git a/src/elements/conditions/variants/ProductConditionRule.php b/src/elements/conditions/variants/ProductConditionRule.php new file mode 100644 index 0000000000..aebc6c582d --- /dev/null +++ b/src/elements/conditions/variants/ProductConditionRule.php @@ -0,0 +1,68 @@ + + * @since 5.3.0 + */ +class ProductConditionRule extends BaseElementSelectConditionRule implements ElementConditionRuleInterface +{ + /** + * @inheritdoc + */ + protected function elementType(): string + { + return Product::class; + } + + /** + * @inheritdoc + */ + public function getLabel(): string + { + return Craft::t('commerce', 'Product'); + } + + /** + * @inheritdoc + */ + public function getExclusiveQueryParams(): array + { + return ['product', 'productId', 'primaryOwnerId', 'primaryOwner', 'owner', 'ownerId']; + } + + /** + * @inheritdoc + */ + public function modifyQuery(ElementQueryInterface $query): void + { + /** @var VariantQuery $query */ + $query->ownerId($this->getElementId()); + } + + /** + * @inheritdoc + */ + public function matchElement(ElementInterface $element): bool + { + /** @var Variant $element */ + return $element->getOwnerId() == $this->getElementId(); + } +} \ No newline at end of file diff --git a/src/elements/conditions/variants/VariantCondition.php b/src/elements/conditions/variants/VariantCondition.php index 311b7baffd..49c3530b53 100644 --- a/src/elements/conditions/variants/VariantCondition.php +++ b/src/elements/conditions/variants/VariantCondition.php @@ -25,6 +25,7 @@ class VariantCondition extends ElementCondition protected function selectableConditionRules(): array { return array_merge(parent::selectableConditionRules(), [ + ProductConditionRule::class, SkuConditionRule::class, ]); } From b6c5f036354911c40034ab5a39f32c073e87f0f1 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Wed, 11 Dec 2024 08:21:20 +0000 Subject: [PATCH 69/69] fix cs --- src/elements/conditions/variants/ProductConditionRule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/elements/conditions/variants/ProductConditionRule.php b/src/elements/conditions/variants/ProductConditionRule.php index aebc6c582d..c2464ab969 100644 --- a/src/elements/conditions/variants/ProductConditionRule.php +++ b/src/elements/conditions/variants/ProductConditionRule.php @@ -65,4 +65,4 @@ public function matchElement(ElementInterface $element): bool /** @var Variant $element */ return $element->getOwnerId() == $this->getElementId(); } -} \ No newline at end of file +}