From 27eb5474b358be5710f1feb8271e93916fd53657 Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Tue, 28 Jul 2026 00:00:26 +0400 Subject: [PATCH 1/2] Add catalog.productPropertyFeature methods --- .php-cs-fixer.php | 1 + CHANGELOG.md | 9 + Makefile | 4 + phpunit.xml.dist | 3 + .../Catalog/Result/CatalogItemResult.php | 2 +- .../Catalog/Catalog/Result/CatalogResult.php | 2 +- .../Catalog/Catalog/Result/CatalogsResult.php | 2 +- .../Catalog/Catalog/Service/Catalog.php | 2 +- .../Catalog/CatalogServiceBuilder.php | 22 +- src/Services/Catalog/Common/ProductType.php | 2 +- .../Common/Result/AbstractCatalogItem.php | 2 +- .../Product/Result/ProductItemResult.php | 2 +- .../Catalog/Product/Result/ProductResult.php | 2 +- .../Catalog/Product/Result/ProductsResult.php | 2 +- .../Catalog/Product/Service/Batch.php | 6 +- .../Catalog/Product/Service/Product.php | 10 +- .../Catalog/ProductPropertyFeature/Batch.php | 47 ++++ .../Result/AvailableFeatureItemResult.php | 25 ++ .../Result/AvailableFeaturesResult.php | 35 +++ ...ProductPropertyFeatureAddedBatchResult.php | 25 ++ .../ProductPropertyFeatureAddedResult.php | 27 ++ .../ProductPropertyFeatureFieldsResult.php | 29 ++ .../ProductPropertyFeatureItemResult.php | 27 ++ .../Result/ProductPropertyFeatureResult.php | 30 +++ ...oductPropertyFeatureUpdatedBatchResult.php | 25 ++ .../ProductPropertyFeatureUpdatedResult.php | 38 +++ .../Result/ProductPropertyFeaturesResult.php | 35 +++ .../ProductPropertyFeature/Service/Batch.php | 141 ++++++++++ .../Service/ProductPropertyFeature.php | 199 ++++++++++++++ .../ProductPropertyFeatureItemResultTest.php | 105 ++++++++ .../Service/BatchTest.php | 151 +++++++++++ .../Service/ProductPropertyFeatureTest.php | 137 ++++++++++ .../Service/BatchTest.php | 95 +++++++ .../Service/ProductPropertyFeatureTest.php | 249 ++++++++++++++++++ 34 files changed, 1475 insertions(+), 18 deletions(-) create mode 100644 src/Services/Catalog/ProductPropertyFeature/Batch.php create mode 100644 src/Services/Catalog/ProductPropertyFeature/Result/AvailableFeatureItemResult.php create mode 100644 src/Services/Catalog/ProductPropertyFeature/Result/AvailableFeaturesResult.php create mode 100644 src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureAddedBatchResult.php create mode 100644 src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureAddedResult.php create mode 100644 src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureFieldsResult.php create mode 100644 src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResult.php create mode 100644 src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureResult.php create mode 100644 src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureUpdatedBatchResult.php create mode 100644 src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureUpdatedResult.php create mode 100644 src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeaturesResult.php create mode 100644 src/Services/Catalog/ProductPropertyFeature/Service/Batch.php create mode 100644 src/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeature.php create mode 100644 tests/Integration/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResultTest.php create mode 100644 tests/Integration/Services/Catalog/ProductPropertyFeature/Service/BatchTest.php create mode 100644 tests/Integration/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php create mode 100644 tests/Unit/Services/Catalog/ProductPropertyFeature/Service/BatchTest.php create mode 100644 tests/Unit/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 6e5393c3..15e131ad 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -29,6 +29,7 @@ ->in(__DIR__ . '/src/Services/SonetGroup/') ->in(__DIR__ . '/src/Services/IMOpenLines/') ->in(__DIR__ . '/src/Services/Landing/') + ->in(__DIR__ . '/src/Services/Catalog/') ->name('*.php') ->exclude(['vendor', 'storage', 'docker', 'docs']) // Exclude directories ->ignoreDotFiles(true) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4bc3b52..6cfc732e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ ### Added +- Added service `Services\Catalog\ProductPropertyFeature` with support for `catalog.productPropertyFeature.*` + methods, + see [catalog.productPropertyFeature.* methods](https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/index.html) ([#553](https://github.com/bitrix24/b24phpsdk/issues/553)): + - `add` adds a parameter (feature) for a product or variation property + - `update` updates a parameter of a product or variation property by id + - `get` returns a product or variation property parameter by id + - `list` returns the list of product/variation property parameters matching the filter + - `getAvailableFeaturesByProperty` returns the list of available parameters for a given property + - `getFields` returns the description of product/variation property parameter fields - Added service `Services\Landing\Site\Service\Site` with support methods, see [landing.site.* methods](https://github.com/bitrix24/b24phpsdk/issues/267): - `add` adds a site diff --git a/Makefile b/Makefile index c2fc4a9a..f0b19198 100644 --- a/Makefile +++ b/Makefile @@ -492,6 +492,10 @@ test-integration-landing-role: test-integration-landing-repowidget: docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_landing_repowidget +.PHONY: test-integration-catalog-product-property-feature +test-integration-catalog-product-property-feature: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_catalog_product_property_feature + # work dev environment .PHONY: php-dev-server-up php-dev-server-up: diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d0187ae5..52888893 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -262,6 +262,9 @@ ./tests/Integration/Services/Landing/RepoWidget/ + + ./tests/Integration/Services/Catalog/ProductPropertyFeature/ + diff --git a/src/Services/Catalog/Catalog/Result/CatalogItemResult.php b/src/Services/Catalog/Catalog/Result/CatalogItemResult.php index e62fc7cb..189852bb 100644 --- a/src/Services/Catalog/Catalog/Result/CatalogItemResult.php +++ b/src/Services/Catalog/Catalog/Result/CatalogItemResult.php @@ -29,4 +29,4 @@ */ class CatalogItemResult extends AbstractCatalogItem { -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Catalog/Result/CatalogResult.php b/src/Services/Catalog/Catalog/Result/CatalogResult.php index aa468aa8..c5222f11 100644 --- a/src/Services/Catalog/Catalog/Result/CatalogResult.php +++ b/src/Services/Catalog/Catalog/Result/CatalogResult.php @@ -21,4 +21,4 @@ public function catalog(): CatalogItemResult { return new CatalogItemResult($this->getCoreResponse()->getResponseData()->getResult()['catalog']); } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Catalog/Result/CatalogsResult.php b/src/Services/Catalog/Catalog/Result/CatalogsResult.php index add5f259..f721c3bb 100644 --- a/src/Services/Catalog/Catalog/Result/CatalogsResult.php +++ b/src/Services/Catalog/Catalog/Result/CatalogsResult.php @@ -32,4 +32,4 @@ public function getCatalogs(): array return $res; } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Catalog/Service/Catalog.php b/src/Services/Catalog/Catalog/Service/Catalog.php index d9cb55fe..0da76ea3 100644 --- a/src/Services/Catalog/Catalog/Service/Catalog.php +++ b/src/Services/Catalog/Catalog/Service/Catalog.php @@ -82,4 +82,4 @@ public function fields(): FieldsResult { return new FieldsResult($this->core->call('catalog.catalog.getFields')); } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/CatalogServiceBuilder.php b/src/Services/Catalog/CatalogServiceBuilder.php index 57c91b88..b28d9366 100644 --- a/src/Services/Catalog/CatalogServiceBuilder.php +++ b/src/Services/Catalog/CatalogServiceBuilder.php @@ -17,6 +17,7 @@ use Bitrix24\SDK\Core\Credentials\Scope; use Bitrix24\SDK\Services\AbstractServiceBuilder; use Bitrix24\SDK\Services\Catalog; + #[ApiServiceBuilderMetadata(new Scope(['catalog']))] class CatalogServiceBuilder extends AbstractServiceBuilder { @@ -44,4 +45,23 @@ public function catalog(): Catalog\Catalog\Service\Catalog return $this->serviceCache[__METHOD__]; } -} \ No newline at end of file + + public function productPropertyFeature(): Catalog\ProductPropertyFeature\Service\ProductPropertyFeature + { + if (!isset($this->serviceCache[__METHOD__])) { + // Use specialized Batch for ProductPropertyFeature to ensure correct REST parameter mapping + // (lowercase 'id' key, unlike the base Batch default of uppercase 'ID') + $productPropertyFeatureBatch = new Catalog\ProductPropertyFeature\Batch( + $this->core, + $this->log + ); + $this->serviceCache[__METHOD__] = new Catalog\ProductPropertyFeature\Service\ProductPropertyFeature( + new Catalog\ProductPropertyFeature\Service\Batch($productPropertyFeatureBatch, $this->log), + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } +} diff --git a/src/Services/Catalog/Common/ProductType.php b/src/Services/Catalog/Common/ProductType.php index 0bf8ef09..ad568a47 100644 --- a/src/Services/Catalog/Common/ProductType.php +++ b/src/Services/Catalog/Common/ProductType.php @@ -20,4 +20,4 @@ enum ProductType: int case SKU = 3; case productOffer = 4; case genericOffer = 5; -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Common/Result/AbstractCatalogItem.php b/src/Services/Catalog/Common/Result/AbstractCatalogItem.php index e2929d73..9761daed 100644 --- a/src/Services/Catalog/Common/Result/AbstractCatalogItem.php +++ b/src/Services/Catalog/Common/Result/AbstractCatalogItem.php @@ -109,4 +109,4 @@ protected function getKeyWithUserfieldByFieldName(string $fieldName) return $this->$fieldName; } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Product/Result/ProductItemResult.php b/src/Services/Catalog/Product/Result/ProductItemResult.php index c576d9b7..58c80851 100644 --- a/src/Services/Catalog/Product/Result/ProductItemResult.php +++ b/src/Services/Catalog/Product/Result/ProductItemResult.php @@ -52,4 +52,4 @@ */ class ProductItemResult extends AbstractCatalogItem { -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Product/Result/ProductResult.php b/src/Services/Catalog/Product/Result/ProductResult.php index 2751fda8..fc83fa8c 100644 --- a/src/Services/Catalog/Product/Result/ProductResult.php +++ b/src/Services/Catalog/Product/Result/ProductResult.php @@ -26,4 +26,4 @@ public function product(): ProductItemResult return new ProductItemResult($this->getCoreResponse()->getResponseData()->getResult()['product']); } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Product/Result/ProductsResult.php b/src/Services/Catalog/Product/Result/ProductsResult.php index aa644fa3..268d6d8f 100644 --- a/src/Services/Catalog/Product/Result/ProductsResult.php +++ b/src/Services/Catalog/Product/Result/ProductsResult.php @@ -31,4 +31,4 @@ public function getProducts(): array return $res; } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Product/Service/Batch.php b/src/Services/Catalog/Product/Service/Batch.php index 45dddcb7..42970546 100644 --- a/src/Services/Catalog/Product/Service/Batch.php +++ b/src/Services/Catalog/Product/Service/Batch.php @@ -25,7 +25,7 @@ { public function __construct( protected BatchOperationsInterface $batch, - protected LoggerInterface $log) - { + protected LoggerInterface $log + ) { } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Product/Service/Product.php b/src/Services/Catalog/Product/Service/Product.php index 730fd7c9..d37d34f6 100644 --- a/src/Services/Catalog/Product/Service/Product.php +++ b/src/Services/Catalog/Product/Service/Product.php @@ -25,7 +25,6 @@ use Bitrix24\SDK\Services\Catalog\Common\ProductType; use Bitrix24\SDK\Services\Catalog\Product\Result\ProductResult; use Bitrix24\SDK\Services\Catalog\Product\Result\ProductsResult; - use Psr\Log\LoggerInterface; #[ApiServiceMetadata(new Scope(['catalog']))] @@ -35,8 +34,7 @@ public function __construct( public Batch $batch, CoreInterface $core, LoggerInterface $logger - ) - { + ) { parent::__construct($core, $logger); } @@ -71,7 +69,9 @@ public function get(int $productId): ProductResult )] public function add(array $productFields): ProductResult { - return new ProductResult($this->core->call('catalog.product.add', [ + return new ProductResult($this->core->call( + 'catalog.product.add', + [ 'fields' => $productFields ] )); @@ -140,4 +140,4 @@ public function fieldsByFilter(int $iblockId, ProductType $productType, ?array $ return new FieldsResult($this->core->call('catalog.product.getFieldsByFilter', ['filter' => $filter])); } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Batch.php b/src/Services/Catalog/ProductPropertyFeature/Batch.php new file mode 100644 index 00000000..c4bccb19 --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Batch.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature; + +use Bitrix24\SDK\Core\Response\DTO\ResponseData; + +/** + * Class Batch + * + * Overrides base Batch to handle differences in catalog.productPropertyFeature.* REST methods: + * - the id key is lowercase 'id', not 'ID' as assumed by the base class default + * - catalog.productPropertyFeature.list wraps list items under the 'productPropertyFeatures' key + * instead of returning a flat array in 'result', as the base class assumes for non-CRM methods + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-list.html + */ +class Batch extends \Bitrix24\SDK\Core\Batch +{ + /** + * Determines the ID key — lowercase 'id' for catalog.productPropertyFeature.* + */ + #[\Override] + protected function determineKeyId(string $apiMethod, ?array $additionalParameters): string + { + return 'id'; + } + + /** + * Extracts list items from the 'productPropertyFeatures' key of the batch/list result + */ + #[\Override] + protected function extractElementsFromBatchResult(ResponseData $responseData, bool $isCrmItemsInBatch): array + { + return $responseData->getResult()['productPropertyFeatures'] ?? []; + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/AvailableFeatureItemResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/AvailableFeatureItemResult.php new file mode 100644 index 00000000..f7fa1be2 --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/AvailableFeatureItemResult.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Result\AbstractAnnotatedItem; + +/** + * @property-read string $featureId + * @property-read string $featureName + * @property-read string $moduleId + */ +class AvailableFeatureItemResult extends AbstractAnnotatedItem +{ +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/AvailableFeaturesResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/AvailableFeaturesResult.php new file mode 100644 index 00000000..48c941ee --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/AvailableFeaturesResult.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class AvailableFeaturesResult extends AbstractResult +{ + /** + * @return AvailableFeatureItemResult[] + * @throws BaseException + */ + public function features(): array + { + $items = []; + $result = $this->getCoreResponse()->getResponseData()->getResult(); + foreach (($result['features'] ?? []) as $item) { + $items[] = new AvailableFeatureItemResult($item); + } + + return $items; + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureAddedBatchResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureAddedBatchResult.php new file mode 100644 index 00000000..48a5ccf6 --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureAddedBatchResult.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Result\AddedItemBatchResult; + +class ProductPropertyFeatureAddedBatchResult extends AddedItemBatchResult +{ + #[\Override] + public function getId(): int + { + return (int)($this->getResponseData()->getResult()['productPropertyFeature']['id'] ?? 0); + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureAddedResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureAddedResult.php new file mode 100644 index 00000000..36da9e5f --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureAddedResult.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; + +class ProductPropertyFeatureAddedResult extends ProductPropertyFeatureResult +{ + /** + * @throws BaseException + */ + public function getId(): int + { + return (int)($this->productPropertyFeature()->id ?? 0); + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureFieldsResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureFieldsResult.php new file mode 100644 index 00000000..722dd97d --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureFieldsResult.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\FieldsResult; + +class ProductPropertyFeatureFieldsResult extends FieldsResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function getFieldsDescription(): array + { + return $this->getCoreResponse()->getResponseData()->getResult()['productPropertyFeature'] ?? []; + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResult.php new file mode 100644 index 00000000..5217da71 --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResult.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Result\AbstractAnnotatedItem; + +/** + * @property-read int $id + * @property-read int $propertyId + * @property-read string $moduleId + * @property-read string $featureId + * @property-read bool $isEnabled + */ +class ProductPropertyFeatureItemResult extends AbstractAnnotatedItem +{ +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureResult.php new file mode 100644 index 00000000..7cd312a9 --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureResult.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class ProductPropertyFeatureResult extends AbstractResult +{ + /** + * @throws BaseException + */ + public function productPropertyFeature(): ProductPropertyFeatureItemResult + { + return new ProductPropertyFeatureItemResult( + $this->getCoreResponse()->getResponseData()->getResult()['productPropertyFeature'] ?? [] + ); + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureUpdatedBatchResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureUpdatedBatchResult.php new file mode 100644 index 00000000..99909db7 --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureUpdatedBatchResult.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Result\UpdatedItemBatchResult; + +class ProductPropertyFeatureUpdatedBatchResult extends UpdatedItemBatchResult +{ + #[\Override] + public function isSuccess(): bool + { + return isset($this->getResponseData()->getResult()['productPropertyFeature']); + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureUpdatedResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureUpdatedResult.php new file mode 100644 index 00000000..93314758 --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureUpdatedResult.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class ProductPropertyFeatureUpdatedResult extends AbstractResult +{ + /** + * @throws BaseException + */ + public function productPropertyFeature(): ProductPropertyFeatureItemResult + { + return new ProductPropertyFeatureItemResult( + $this->getCoreResponse()->getResponseData()->getResult()['productPropertyFeature'] ?? [] + ); + } + + /** + * @throws BaseException + */ + public function isSuccess(): bool + { + return isset($this->getCoreResponse()->getResponseData()->getResult()['productPropertyFeature']); + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeaturesResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeaturesResult.php new file mode 100644 index 00000000..319a083b --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeaturesResult.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class ProductPropertyFeaturesResult extends AbstractResult +{ + /** + * @return ProductPropertyFeatureItemResult[] + * @throws BaseException + */ + public function productPropertyFeatures(): array + { + $items = []; + $result = $this->getCoreResponse()->getResponseData()->getResult(); + foreach (($result['productPropertyFeatures'] ?? []) as $item) { + $items[] = new ProductPropertyFeatureItemResult($item); + } + + return $items; + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Service/Batch.php b/src/Services/Catalog/ProductPropertyFeature/Service/Batch.php new file mode 100644 index 00000000..5001f8ab --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Service/Batch.php @@ -0,0 +1,141 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Service; + +use Bitrix24\SDK\Attributes\ApiBatchMethodMetadata; +use Bitrix24\SDK\Attributes\ApiBatchServiceMetadata; +use Bitrix24\SDK\Core\Contracts\BatchOperationsInterface; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureAddedBatchResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureItemResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureUpdatedBatchResult; +use Generator; +use Psr\Log\LoggerInterface; + +#[ApiBatchServiceMetadata(new Scope(['catalog']))] +class Batch +{ + public function __construct(protected BatchOperationsInterface $batch, protected LoggerInterface $log) + { + } + + /** + * Batch list product/variation property parameters. + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-list.html + * + * @param array $order + * @param array $filter + * @param array $select + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'catalog.productPropertyFeature.list', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-list.html', + 'Batch list product/variation property parameters' + )] + public function list(array $order = [], array $filter = [], array $select = [], ?int $limit = null): Generator + { + $this->log->debug( + 'batchList', + [ + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'limit' => $limit, + ] + ); + + foreach ( + $this->batch->getTraversableListWithCount( + 'catalog.productPropertyFeature.list', + $order, + $filter, + $select, + $limit + ) as $key => $value + ) { + yield $key => new ProductPropertyFeatureItemResult($value); + } + } + + /** + * Batch add product/variation property parameters. + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-add.html + * + * @param array $productPropertyFeatures + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'catalog.productPropertyFeature.add', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-add.html', + 'Batch add product/variation property parameters' + )] + public function add(array $productPropertyFeatures): Generator + { + $items = []; + foreach ($productPropertyFeatures as $item) { + $items[] = [ + 'fields' => $item, + ]; + } + + foreach ($this->batch->addEntityItems('catalog.productPropertyFeature.add', $items) as $key => $item) { + yield $key => new ProductPropertyFeatureAddedBatchResult($item); + } + } + + /** + * Batch update product/variation property parameters. + * + * Update elements in array with structure: + * id => [ + * 'fields' => [] // fields to update + * ] + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-update.html + * + * @param array $entityItems + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'catalog.productPropertyFeature.update', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-update.html', + 'Batch update product/variation property parameters' + )] + public function update(array $entityItems): Generator + { + foreach ( + $this->batch->updateEntityItems( + 'catalog.productPropertyFeature.update', + $entityItems + ) as $key => $item + ) { + yield $key => new ProductPropertyFeatureUpdatedBatchResult($item); + } + } +} diff --git a/src/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeature.php b/src/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeature.php new file mode 100644 index 00000000..35770c53 --- /dev/null +++ b/src/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeature.php @@ -0,0 +1,199 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Service; + +use Bitrix24\SDK\Attributes\ApiEndpointMetadata; +use Bitrix24\SDK\Attributes\ApiServiceMetadata; +use Bitrix24\SDK\Core\Contracts\CoreInterface; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\AvailableFeaturesResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureAddedResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureFieldsResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureUpdatedResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeaturesResult; +use Psr\Log\LoggerInterface; + +#[ApiServiceMetadata(new Scope(['catalog']))] +class ProductPropertyFeature extends AbstractService +{ + public function __construct(public Batch $batch, CoreInterface $core, LoggerInterface $logger) + { + parent::__construct($core, $logger); + } + + /** + * Adds a parameter (feature) for a product or variation property. + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-add.html + * + * @param array{ + * propertyId: int, + * moduleId: string, + * featureId: string, + * isEnabled: string, + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.productPropertyFeature.add', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-add.html', + 'Adds a parameter of a product or variation property' + )] + public function add(array $fields): ProductPropertyFeatureAddedResult + { + return new ProductPropertyFeatureAddedResult( + $this->core->call('catalog.productPropertyFeature.add', [ + 'fields' => $fields, + ]) + ); + } + + /** + * Updates a parameter of a product or variation property by id. + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-update.html + * + * @param array{ + * propertyId: int, + * moduleId: string, + * featureId: string, + * isEnabled: string, + * } $fields + * + * @throws BaseException + * @throws InvalidArgumentException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.productPropertyFeature.update', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-update.html', + 'Updates a parameter of a product or variation property' + )] + public function update(int $id, array $fields): ProductPropertyFeatureUpdatedResult + { + $this->guardPositiveId($id); + + return new ProductPropertyFeatureUpdatedResult( + $this->core->call('catalog.productPropertyFeature.update', [ + 'id' => $id, + 'fields' => $fields, + ]) + ); + } + + /** + * Returns a product or variation property parameter by id. + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-get.html + * + * @throws BaseException + * @throws InvalidArgumentException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.productPropertyFeature.get', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-get.html', + 'Returns a product or variation property parameter by id' + )] + public function get(int $id): ProductPropertyFeatureResult + { + $this->guardPositiveId($id); + + return new ProductPropertyFeatureResult( + $this->core->call('catalog.productPropertyFeature.get', [ + 'id' => $id, + ]) + ); + } + + /** + * Returns the list of product/variation property parameters matching the filter. + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-list.html + * + * @param array $select Fields to select + * @param array $filter Filter map + * @param array $order Sort order map + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.productPropertyFeature.list', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-list.html', + 'Returns the list of product/variation property parameters' + )] + public function list(array $select = [], array $filter = [], array $order = []): ProductPropertyFeaturesResult + { + return new ProductPropertyFeaturesResult( + $this->core->call('catalog.productPropertyFeature.list', [ + 'select' => $select, + 'filter' => $filter, + 'order' => $order, + ]) + ); + } + + /** + * Returns the list of available parameters (features) for the given product or variation property. + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-get-available-features-by-property.html + * + * @throws BaseException + * @throws InvalidArgumentException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.productPropertyFeature.getAvailableFeaturesByProperty', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-get-available-features-by-property.html', + 'Returns the list of available parameters for the given product or variation property' + )] + public function getAvailableFeaturesByProperty(int $propertyId): AvailableFeaturesResult + { + $this->guardPositiveId($propertyId); + + return new AvailableFeaturesResult( + $this->core->call('catalog.productPropertyFeature.getAvailableFeaturesByProperty', [ + 'propertyId' => $propertyId, + ]) + ); + } + + /** + * Returns the description of product/variation property parameter fields. + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-get-fields.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.productPropertyFeature.getFields', + 'https://apidocs.bitrix24.com/api-reference/catalog/product-property-feature/catalog-product-property-feature-get-fields.html', + 'Returns the description of product/variation property parameter fields' + )] + public function getFields(): ProductPropertyFeatureFieldsResult + { + return new ProductPropertyFeatureFieldsResult( + $this->core->call('catalog.productPropertyFeature.getFields') + ); + } +} diff --git a/tests/Integration/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResultTest.php b/tests/Integration/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResultTest.php new file mode 100644 index 00000000..28d8bb52 --- /dev/null +++ b/tests/Integration/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResultTest.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\Catalog\ProductPropertyFeature\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureItemResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Service\ProductPropertyFeature; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Fabric; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(ProductPropertyFeatureItemResult::class)] +class ProductPropertyFeatureItemResultTest extends TestCase +{ + use CustomBitrix24Assertions; + + private ProductPropertyFeature $productPropertyFeatureService; + + private int $propertyId; + + #[\Override] + protected function setUp(): void + { + $this->productPropertyFeatureService = Fabric::getServiceBuilder()->getCatalogScope()->productPropertyFeature(); + $this->propertyId = $this->createProductProperty(); + } + + #[\Override] + protected function tearDown(): void + { + $this->deleteProductProperty($this->propertyId); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[Test] + #[TestDox('all fields in ProductPropertyFeatureItemResult are annotated in phpdoc and match with raw api response')] + public function testAllFieldsAreAnnotated(): void + { + $rawFields = $this->productPropertyFeatureService->getFields()->getFieldsDescription(); + + $this->assertBitrix24AllResultItemFieldsAnnotated(array_keys($rawFields), ProductPropertyFeatureItemResult::class); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[Test] + #[TestDox('all fields in ProductPropertyFeatureItemResult have valid type casting in magic getters')] + public function testAllFieldsHasValidTypeCastingInMagicGetters(): void + { + $item = $this->productPropertyFeatureService->add([ + 'propertyId' => $this->propertyId, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'Y', + ])->productPropertyFeature(); + + $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations($item, ProductPropertyFeatureItemResult::class); + } + + protected function createProductProperty(): int + { + $core = Fabric::getCore(); + $iblockId = (int)$core->call('catalog.catalog.list', [ + 'select' => ['id', 'iblockId'], + ])->getResponseData()->getResult()['catalogs'][0]['iblockId']; + + return (int)$core->call('catalog.productProperty.add', [ + 'fields' => [ + 'iblockId' => $iblockId, + 'name' => 'SDK Test Property ' . uniqid('', true), + 'propertyType' => 'S', + 'active' => 'Y', + 'sort' => 100, + ], + ])->getResponseData()->getResult()['productProperty']['id']; + } + + protected function deleteProductProperty(int $id): void + { + $core = Fabric::getCore(); + $core->call('catalog.productProperty.delete', [ + 'id' => $id, + ]); + } +} diff --git a/tests/Integration/Services/Catalog/ProductPropertyFeature/Service/BatchTest.php b/tests/Integration/Services/Catalog/ProductPropertyFeature/Service/BatchTest.php new file mode 100644 index 00000000..1c2a840b --- /dev/null +++ b/tests/Integration/Services/Catalog/ProductPropertyFeature/Service/BatchTest.php @@ -0,0 +1,151 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\Catalog\ProductPropertyFeature\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureItemResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Service\Batch; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Service\ProductPropertyFeature; +use Bitrix24\SDK\Tests\Integration\Fabric; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Batch::class)] +class BatchTest extends TestCase +{ + private ProductPropertyFeature $service; + + /** @var int[] */ + private array $propertyIds = []; + + #[\Override] + protected function setUp(): void + { + $this->service = Fabric::getServiceBuilder()->getCatalogScope()->productPropertyFeature(); + } + + #[\Override] + protected function tearDown(): void + { + foreach ($this->propertyIds as $propertyId) { + $this->deleteProductProperty($propertyId); + } + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testBatchAddAndList(): void + { + $propertyId = $this->createProductProperty(); + + $items = [ + [ + 'propertyId' => $propertyId, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'Y', + ], + [ + 'propertyId' => $propertyId, + 'moduleId' => 'iblock', + 'featureId' => 'DETAIL_PAGE_SHOW', + 'isEnabled' => 'N', + ], + ]; + + $addedIds = []; + foreach ($this->service->batch->add($items) as $result) { + $addedIds[] = $result->getId(); + self::assertGreaterThan(0, $result->getId()); + } + + self::assertCount(2, $addedIds); + + $found = []; + foreach ($this->service->batch->list(['id' => 'asc'], ['propertyId' => $propertyId]) as $item) { + self::assertInstanceOf(ProductPropertyFeatureItemResult::class, $item); + $found[] = $item->id; + } + + foreach ($addedIds as $addedId) { + self::assertContains($addedId, $found); + } + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testBatchUpdate(): void + { + $propertyId = $this->createProductProperty(); + + $addedId = $this->service->add([ + 'propertyId' => $propertyId, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'Y', + ])->getId(); + + $entityItems = [ + $addedId => [ + 'fields' => [ + 'propertyId' => $propertyId, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'N', + ], + ], + ]; + + foreach ($this->service->batch->update($entityItems) as $result) { + self::assertTrue($result->isSuccess()); + } + + self::assertFalse($this->service->get($addedId)->productPropertyFeature()->isEnabled); + } + + protected function createProductProperty(): int + { + $core = Fabric::getCore(); + $iblockId = (int)$core->call('catalog.catalog.list', [ + 'select' => ['id', 'iblockId'], + ])->getResponseData()->getResult()['catalogs'][0]['iblockId']; + + $propertyId = (int)$core->call('catalog.productProperty.add', [ + 'fields' => [ + 'iblockId' => $iblockId, + 'name' => 'SDK Batch Test Property ' . uniqid('', true), + 'propertyType' => 'S', + 'active' => 'Y', + 'sort' => 100, + ], + ])->getResponseData()->getResult()['productProperty']['id']; + + $this->propertyIds[] = $propertyId; + + return $propertyId; + } + + protected function deleteProductProperty(int $id): void + { + $core = Fabric::getCore(); + $core->call('catalog.productProperty.delete', [ + 'id' => $id, + ]); + } +} diff --git a/tests/Integration/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php b/tests/Integration/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php new file mode 100644 index 00000000..af3b99d9 --- /dev/null +++ b/tests/Integration/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\Catalog\ProductPropertyFeature\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureItemResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Service\ProductPropertyFeature; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Fabric; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\TestCase; + +#[CoversMethod(ProductPropertyFeature::class, 'add')] +#[CoversMethod(ProductPropertyFeature::class, 'update')] +#[CoversMethod(ProductPropertyFeature::class, 'get')] +#[CoversMethod(ProductPropertyFeature::class, 'list')] +#[CoversMethod(ProductPropertyFeature::class, 'getAvailableFeaturesByProperty')] +#[CoversMethod(ProductPropertyFeature::class, 'getFields')] +#[CoversClass(ProductPropertyFeature::class)] +class ProductPropertyFeatureTest extends TestCase +{ + use CustomBitrix24Assertions; + + private ProductPropertyFeature $service; + + private int $propertyId; + + #[\Override] + protected function setUp(): void + { + $this->service = Fabric::getServiceBuilder()->getCatalogScope()->productPropertyFeature(); + $this->propertyId = $this->createProductProperty(); + } + + #[\Override] + protected function tearDown(): void + { + $this->deleteProductProperty($this->propertyId); + } + + public function testFields(): void + { + $fields = $this->service->getFields()->getFieldsDescription(); + self::assertIsArray($fields); + $this->assertNotEmpty($fields); + $this->assertBitrix24AllResultItemFieldsAnnotated(array_keys($fields), ProductPropertyFeatureItemResult::class); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testAddGetUpdateList(): void + { + $addedResult = $this->service->add([ + 'propertyId' => $this->propertyId, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'Y', + ]); + $id = $addedResult->getId(); + self::assertGreaterThan(0, $id); + + $item = $this->service->get($id)->productPropertyFeature(); + self::assertEquals($this->propertyId, $item->propertyId); + self::assertEquals('iblock', $item->moduleId); + self::assertEquals('LIST_PAGE_SHOW', $item->featureId); + self::assertTrue($item->isEnabled); + + $updatedResult = $this->service->update($id, [ + 'propertyId' => $this->propertyId, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'N', + ]); + self::assertTrue($updatedResult->isSuccess()); + self::assertFalse($this->service->get($id)->productPropertyFeature()->isEnabled); + + $list = $this->service->list( + ['id', 'propertyId', 'moduleId', 'featureId', 'isEnabled'], + ['propertyId' => $this->propertyId], + ['id' => 'asc'] + )->productPropertyFeatures(); + self::assertNotEmpty($list); + self::assertEquals($id, $list[0]->id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testGetAvailableFeaturesByProperty(): void + { + $features = $this->service->getAvailableFeaturesByProperty($this->propertyId)->features(); + self::assertNotEmpty($features); + self::assertEquals('iblock', $features[0]->moduleId); + } + + protected function createProductProperty(): int + { + $core = Fabric::getCore(); + $iblockId = (int)$core->call('catalog.catalog.list', [ + 'select' => ['id', 'iblockId'], + ])->getResponseData()->getResult()['catalogs'][0]['iblockId']; + + return (int)$core->call('catalog.productProperty.add', [ + 'fields' => [ + 'iblockId' => $iblockId, + 'name' => 'SDK Test Property ' . uniqid('', true), + 'propertyType' => 'S', + 'active' => 'Y', + 'sort' => 100, + ], + ])->getResponseData()->getResult()['productProperty']['id']; + } + + protected function deleteProductProperty(int $id): void + { + $core = Fabric::getCore(); + $core->call('catalog.productProperty.delete', [ + 'id' => $id, + ]); + } +} diff --git a/tests/Unit/Services/Catalog/ProductPropertyFeature/Service/BatchTest.php b/tests/Unit/Services/Catalog/ProductPropertyFeature/Service/BatchTest.php new file mode 100644 index 00000000..7e2dc1a8 --- /dev/null +++ b/tests/Unit/Services/Catalog/ProductPropertyFeature/Service/BatchTest.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Unit\Services\Catalog\ProductPropertyFeature\Service; + +use Bitrix24\SDK\Core\Contracts\BatchOperationsInterface; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureAddedBatchResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureItemResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureUpdatedBatchResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Service\Batch; +use Bitrix24\SDK\Tests\Unit\Stubs\NullBatch; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; +use Psr\Log\NullLogger; + +#[CoversClass(Batch::class)] +class BatchTest extends TestCase +{ + #[Test] + #[TestDox('list() yields ProductPropertyFeatureItemResult items')] + public function testListYieldsItemResults(): void + { + $batch = new Batch(new NullBatch(), new NullLogger()); + + foreach ($batch->list() as $item) { + $this->assertInstanceOf(ProductPropertyFeatureItemResult::class, $item); + } + + $this->addToAssertionCount(1); + } + + #[Test] + #[TestDox('add() yields ProductPropertyFeatureAddedBatchResult items and forwards fields wrapper')] + public function testAddYieldsAddedBatchResults(): void + { + $batchOperations = $this->createMock(BatchOperationsInterface::class); + $batchOperations->expects($this->once()) + ->method('addEntityItems') + ->with( + 'catalog.productPropertyFeature.add', + [ + [ + 'fields' => [ + 'propertyId' => 901, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'Y', + ], + ], + ] + ) + ->willReturn((static function (): \Generator { + yield from []; + })()); + + $batch = new Batch($batchOperations, new NullLogger()); + foreach ($batch->add([ + [ + 'propertyId' => 901, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'Y', + ], + ]) as $item) { + $this->assertInstanceOf(ProductPropertyFeatureAddedBatchResult::class, $item); + } + + $this->addToAssertionCount(1); + } + + #[Test] + #[TestDox('update() yields ProductPropertyFeatureUpdatedBatchResult items')] + public function testUpdateYieldsUpdatedBatchResults(): void + { + $batch = new Batch(new NullBatch(), new NullLogger()); + + foreach ($batch->update([101 => ['fields' => ['isEnabled' => 'N']]]) as $item) { + $this->assertInstanceOf(ProductPropertyFeatureUpdatedBatchResult::class, $item); + } + + $this->addToAssertionCount(1); + } +} diff --git a/tests/Unit/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php b/tests/Unit/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php new file mode 100644 index 00000000..d174f30c --- /dev/null +++ b/tests/Unit/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php @@ -0,0 +1,249 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Unit\Services\Catalog\ProductPropertyFeature\Service; + +use Bitrix24\SDK\Core\ApiLevelErrorHandler; +use Bitrix24\SDK\Core\Commands\Command; +use Bitrix24\SDK\Core\Contracts\CoreInterface; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Response\Response; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\AvailableFeaturesResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureAddedResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureFieldsResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureUpdatedResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeaturesResult; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Service\Batch; +use Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Service\ProductPropertyFeature; +use Bitrix24\SDK\Tests\Unit\Stubs\NullBatch; +use Bitrix24\SDK\Tests\Unit\Stubs\NullCore; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; +use Psr\Log\NullLogger; +use Symfony\Component\HttpClient\Response\MockResponse; + +#[CoversClass(ProductPropertyFeature::class)] +class ProductPropertyFeatureTest extends TestCase +{ + private ProductPropertyFeature $service; + + #[\Override] + protected function setUp(): void + { + $this->service = new ProductPropertyFeature( + new Batch(new NullBatch(), new NullLogger()), + new NullCore(), + new NullLogger() + ); + } + + #[Test] + public function testAddReturnsProductPropertyFeatureAddedResult(): void + { + $this->assertInstanceOf( + ProductPropertyFeatureAddedResult::class, + $this->service->add([ + 'propertyId' => 901, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'Y', + ]) + ); + } + + #[Test] + public function testUpdateReturnsProductPropertyFeatureUpdatedResult(): void + { + $this->assertInstanceOf( + ProductPropertyFeatureUpdatedResult::class, + $this->service->update(101, [ + 'propertyId' => 901, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'N', + ]) + ); + } + + #[Test] + public function testGetReturnsProductPropertyFeatureResult(): void + { + $this->assertInstanceOf(ProductPropertyFeatureResult::class, $this->service->get(101)); + } + + #[Test] + public function testListReturnsProductPropertyFeaturesResult(): void + { + $this->assertInstanceOf(ProductPropertyFeaturesResult::class, $this->service->list()); + } + + #[Test] + public function testGetAvailableFeaturesByPropertyReturnsAvailableFeaturesResult(): void + { + $this->assertInstanceOf(AvailableFeaturesResult::class, $this->service->getAvailableFeaturesByProperty(901)); + } + + #[Test] + public function testGetFieldsReturnsProductPropertyFeatureFieldsResult(): void + { + $this->assertInstanceOf(ProductPropertyFeatureFieldsResult::class, $this->service->getFields()); + } + + #[Test] + public function testUpdateThrowsExceptionForNonPositiveId(): void + { + $this->expectException(InvalidArgumentException::class); + $this->service->update(0, ['propertyId' => 901, 'moduleId' => 'iblock', 'featureId' => 'X', 'isEnabled' => 'Y']); + } + + #[Test] + public function testGetThrowsExceptionForNonPositiveId(): void + { + $this->expectException(InvalidArgumentException::class); + $this->service->get(0); + } + + #[Test] + public function testGetAvailableFeaturesByPropertyThrowsExceptionForNonPositivePropertyId(): void + { + $this->expectException(InvalidArgumentException::class); + $this->service->getAvailableFeaturesByProperty(0); + } + + #[Test] + #[TestDox('add() calls catalog.productPropertyFeature.add with a nested fields object')] + public function testAddSendsNestedFields(): void + { + [$method, $captured] = $this->call(static fn (ProductPropertyFeature $service) => $service->add([ + 'propertyId' => 901, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'Y', + ])); + + $this->assertSame('catalog.productPropertyFeature.add', $method); + $this->assertSame([ + 'fields' => [ + 'propertyId' => 901, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'Y', + ], + ], $captured); + } + + #[Test] + #[TestDox('update() sends id and nested fields')] + public function testUpdateSendsIdAndFields(): void + { + [$method, $captured] = $this->call(static fn (ProductPropertyFeature $service) => $service->update(101, [ + 'propertyId' => 901, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'N', + ])); + + $this->assertSame('catalog.productPropertyFeature.update', $method); + $this->assertSame(101, $captured['id']); + $this->assertSame([ + 'propertyId' => 901, + 'moduleId' => 'iblock', + 'featureId' => 'LIST_PAGE_SHOW', + 'isEnabled' => 'N', + ], $captured['fields']); + } + + #[Test] + #[TestDox('get() sends the id')] + public function testGetSendsId(): void + { + [$method, $captured] = $this->call(static fn (ProductPropertyFeature $service) => $service->get(101)); + + $this->assertSame('catalog.productPropertyFeature.get', $method); + $this->assertSame(101, $captured['id']); + } + + #[Test] + #[TestDox('list() sends select, filter and order')] + public function testListSendsSelectFilterOrder(): void + { + [$method, $captured] = $this->call(static fn (ProductPropertyFeature $service) => $service->list( + ['id', 'propertyId'], + ['propertyId' => 901], + ['id' => 'ASC'] + )); + + $this->assertSame('catalog.productPropertyFeature.list', $method); + $this->assertSame(['id', 'propertyId'], $captured['select']); + $this->assertSame(['propertyId' => 901], $captured['filter']); + $this->assertSame(['id' => 'ASC'], $captured['order']); + } + + #[Test] + #[TestDox('getAvailableFeaturesByProperty() sends propertyId')] + public function testGetAvailableFeaturesByPropertySendsPropertyId(): void + { + [$method, $captured] = $this->call( + static fn (ProductPropertyFeature $service) => $service->getAvailableFeaturesByProperty(901) + ); + + $this->assertSame('catalog.productPropertyFeature.getAvailableFeaturesByProperty', $method); + $this->assertSame(901, $captured['propertyId']); + } + + #[Test] + #[TestDox('getFields() calls catalog.productPropertyFeature.getFields with no parameters')] + public function testGetFieldsSendsNoParameters(): void + { + [$method, $captured] = $this->call(static fn (ProductPropertyFeature $service) => $service->getFields()); + + $this->assertSame('catalog.productPropertyFeature.getFields', $method); + $this->assertSame([], $captured); + } + + /** + * @return array{0: string, 1: array} + */ + private function call(callable $action): array + { + $method = null; + $captured = []; + $response = new Response( + new MockResponse(''), + new Command('', []), + new ApiLevelErrorHandler(new NullLogger()), + new NullLogger() + ); + + $core = $this->createStub(CoreInterface::class); + $core->method('call')->willReturnCallback( + function (string $apiMethod, array $parameters = []) use (&$method, &$captured, $response): Response { + $method = $apiMethod; + $captured = $parameters; + + return $response; + } + ); + + $action(new ProductPropertyFeature( + new Batch(new NullBatch(), new NullLogger()), + $core, + new NullLogger() + )); + + return [$method, $captured]; + } +} From 6710ebe7fff4beded969ed75e1e61ed4a1e07725 Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Tue, 28 Jul 2026 00:13:59 +0400 Subject: [PATCH 2/2] Fix on liter results --- .../Result/AvailableFeatureItemResult.php | 4 +-- .../ProductPropertyFeatureItemResult.php | 25 +++++++++++++++++-- .../ProductPropertyFeatureItemResultTest.php | 8 ++++-- .../Service/ProductPropertyFeatureTest.php | 18 ++++++------- .../Service/ProductPropertyFeatureTest.php | 12 ++++----- 5 files changed, 46 insertions(+), 21 deletions(-) diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/AvailableFeatureItemResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/AvailableFeatureItemResult.php index f7fa1be2..a8aa02d2 100644 --- a/src/Services/Catalog/ProductPropertyFeature/Result/AvailableFeatureItemResult.php +++ b/src/Services/Catalog/ProductPropertyFeature/Result/AvailableFeatureItemResult.php @@ -13,13 +13,13 @@ namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; -use Bitrix24\SDK\Core\Result\AbstractAnnotatedItem; +use Bitrix24\SDK\Core\Result\AbstractItem; /** * @property-read string $featureId * @property-read string $featureName * @property-read string $moduleId */ -class AvailableFeatureItemResult extends AbstractAnnotatedItem +class AvailableFeatureItemResult extends AbstractItem { } diff --git a/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResult.php b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResult.php index 5217da71..9ced4ecb 100644 --- a/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResult.php +++ b/src/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResult.php @@ -13,7 +13,7 @@ namespace Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result; -use Bitrix24\SDK\Core\Result\AbstractAnnotatedItem; +use Bitrix24\SDK\Core\Result\AbstractItem; /** * @property-read int $id @@ -22,6 +22,27 @@ * @property-read string $featureId * @property-read bool $isEnabled */ -class ProductPropertyFeatureItemResult extends AbstractAnnotatedItem +class ProductPropertyFeatureItemResult extends AbstractItem { + /** + * @param int|string $offset + * + * @return bool|int|mixed|null + */ + public function __get($offset) + { + switch ($offset) { + case 'id': + case 'propertyId': + if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { + return (int)$this->data[$offset]; + } + + return null; + case 'isEnabled': + return $this->data[$offset] === 'Y'; + default: + return $this->data[$offset] ?? null; + } + } } diff --git a/tests/Integration/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResultTest.php b/tests/Integration/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResultTest.php index 28d8bb52..0a97825d 100644 --- a/tests/Integration/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResultTest.php +++ b/tests/Integration/Services/Catalog/ProductPropertyFeature/Result/ProductPropertyFeatureItemResultTest.php @@ -67,14 +67,18 @@ public function testAllFieldsAreAnnotated(): void #[TestDox('all fields in ProductPropertyFeatureItemResult have valid type casting in magic getters')] public function testAllFieldsHasValidTypeCastingInMagicGetters(): void { - $item = $this->productPropertyFeatureService->add([ + $productPropertyFeatureItemResult = $this->productPropertyFeatureService->add([ 'propertyId' => $this->propertyId, 'moduleId' => 'iblock', 'featureId' => 'LIST_PAGE_SHOW', 'isEnabled' => 'Y', ])->productPropertyFeature(); - $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations($item, ProductPropertyFeatureItemResult::class); + self::assertIsInt($productPropertyFeatureItemResult->id); + self::assertIsInt($productPropertyFeatureItemResult->propertyId); + self::assertIsString($productPropertyFeatureItemResult->moduleId); + self::assertIsString($productPropertyFeatureItemResult->featureId); + self::assertIsBool($productPropertyFeatureItemResult->isEnabled); } protected function createProductProperty(): int diff --git a/tests/Integration/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php b/tests/Integration/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php index af3b99d9..78731807 100644 --- a/tests/Integration/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php +++ b/tests/Integration/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php @@ -65,28 +65,28 @@ public function testFields(): void */ public function testAddGetUpdateList(): void { - $addedResult = $this->service->add([ + $productPropertyFeatureAddedResult = $this->service->add([ 'propertyId' => $this->propertyId, 'moduleId' => 'iblock', 'featureId' => 'LIST_PAGE_SHOW', 'isEnabled' => 'Y', ]); - $id = $addedResult->getId(); + $id = $productPropertyFeatureAddedResult->getId(); self::assertGreaterThan(0, $id); - $item = $this->service->get($id)->productPropertyFeature(); - self::assertEquals($this->propertyId, $item->propertyId); - self::assertEquals('iblock', $item->moduleId); - self::assertEquals('LIST_PAGE_SHOW', $item->featureId); - self::assertTrue($item->isEnabled); + $productPropertyFeatureItemResult = $this->service->get($id)->productPropertyFeature(); + self::assertEquals($this->propertyId, $productPropertyFeatureItemResult->propertyId); + self::assertEquals('iblock', $productPropertyFeatureItemResult->moduleId); + self::assertEquals('LIST_PAGE_SHOW', $productPropertyFeatureItemResult->featureId); + self::assertTrue($productPropertyFeatureItemResult->isEnabled); - $updatedResult = $this->service->update($id, [ + $productPropertyFeatureUpdatedResult = $this->service->update($id, [ 'propertyId' => $this->propertyId, 'moduleId' => 'iblock', 'featureId' => 'LIST_PAGE_SHOW', 'isEnabled' => 'N', ]); - self::assertTrue($updatedResult->isSuccess()); + self::assertTrue($productPropertyFeatureUpdatedResult->isSuccess()); self::assertFalse($this->service->get($id)->productPropertyFeature()->isEnabled); $list = $this->service->list( diff --git a/tests/Unit/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php b/tests/Unit/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php index d174f30c..6b9c1a88 100644 --- a/tests/Unit/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php +++ b/tests/Unit/Services/Catalog/ProductPropertyFeature/Service/ProductPropertyFeatureTest.php @@ -127,7 +127,7 @@ public function testGetAvailableFeaturesByPropertyThrowsExceptionForNonPositiveP #[TestDox('add() calls catalog.productPropertyFeature.add with a nested fields object')] public function testAddSendsNestedFields(): void { - [$method, $captured] = $this->call(static fn (ProductPropertyFeature $service) => $service->add([ + [$method, $captured] = $this->call(static fn (ProductPropertyFeature $productPropertyFeature): \Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureAddedResult => $productPropertyFeature->add([ 'propertyId' => 901, 'moduleId' => 'iblock', 'featureId' => 'LIST_PAGE_SHOW', @@ -149,7 +149,7 @@ public function testAddSendsNestedFields(): void #[TestDox('update() sends id and nested fields')] public function testUpdateSendsIdAndFields(): void { - [$method, $captured] = $this->call(static fn (ProductPropertyFeature $service) => $service->update(101, [ + [$method, $captured] = $this->call(static fn (ProductPropertyFeature $productPropertyFeature): \Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureUpdatedResult => $productPropertyFeature->update(101, [ 'propertyId' => 901, 'moduleId' => 'iblock', 'featureId' => 'LIST_PAGE_SHOW', @@ -170,7 +170,7 @@ public function testUpdateSendsIdAndFields(): void #[TestDox('get() sends the id')] public function testGetSendsId(): void { - [$method, $captured] = $this->call(static fn (ProductPropertyFeature $service) => $service->get(101)); + [$method, $captured] = $this->call(static fn (ProductPropertyFeature $productPropertyFeature): \Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureResult => $productPropertyFeature->get(101)); $this->assertSame('catalog.productPropertyFeature.get', $method); $this->assertSame(101, $captured['id']); @@ -180,7 +180,7 @@ public function testGetSendsId(): void #[TestDox('list() sends select, filter and order')] public function testListSendsSelectFilterOrder(): void { - [$method, $captured] = $this->call(static fn (ProductPropertyFeature $service) => $service->list( + [$method, $captured] = $this->call(static fn (ProductPropertyFeature $productPropertyFeature): \Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeaturesResult => $productPropertyFeature->list( ['id', 'propertyId'], ['propertyId' => 901], ['id' => 'ASC'] @@ -197,7 +197,7 @@ public function testListSendsSelectFilterOrder(): void public function testGetAvailableFeaturesByPropertySendsPropertyId(): void { [$method, $captured] = $this->call( - static fn (ProductPropertyFeature $service) => $service->getAvailableFeaturesByProperty(901) + static fn (ProductPropertyFeature $productPropertyFeature): \Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\AvailableFeaturesResult => $productPropertyFeature->getAvailableFeaturesByProperty(901) ); $this->assertSame('catalog.productPropertyFeature.getAvailableFeaturesByProperty', $method); @@ -208,7 +208,7 @@ public function testGetAvailableFeaturesByPropertySendsPropertyId(): void #[TestDox('getFields() calls catalog.productPropertyFeature.getFields with no parameters')] public function testGetFieldsSendsNoParameters(): void { - [$method, $captured] = $this->call(static fn (ProductPropertyFeature $service) => $service->getFields()); + [$method, $captured] = $this->call(static fn (ProductPropertyFeature $productPropertyFeature): \Bitrix24\SDK\Services\Catalog\ProductPropertyFeature\Result\ProductPropertyFeatureFieldsResult => $productPropertyFeature->getFields()); $this->assertSame('catalog.productPropertyFeature.getFields', $method); $this->assertSame([], $captured);