From c0fbc962b265a4edf47f1e322a147788e14d37db Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Thu, 9 Jul 2026 19:41:51 +0400 Subject: [PATCH 1/3] Add catalog.product methods --- .php-cs-fixer.php | 1 + CHANGELOG.md | 39 ++++ Makefile | 30 +++ phpunit.xml.dist | 21 +++ .../Catalog/Result/CatalogItemResult.php | 2 +- .../Catalog/Catalog/Result/CatalogResult.php | 2 +- .../Catalog/Catalog/Result/CatalogsResult.php | 9 +- .../Catalog/Catalog/Service/Catalog.php | 2 +- .../Catalog/CatalogServiceBuilder.php | 43 ++++- src/Services/Catalog/Common/ProductType.php | 4 +- .../Common/Result/AbstractCatalogItem.php | 7 +- .../Product/Offer/Result/OfferItemResult.php | 68 +++++++ .../Product/Offer/Result/OfferResult.php | 24 +++ .../Product/Offer/Result/OffersResult.php | 34 ++++ .../Catalog/Product/Offer/Service/Offer.php | 171 ++++++++++++++++++ .../Result/ProductServiceItemResult.php | 51 ++++++ .../Result/ProductServiceResult.php | 24 +++ .../Result/ProductServicesResult.php | 34 ++++ .../ProductService/Service/ProductService.php | 171 ++++++++++++++++++ .../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 | 74 ++++++-- .../Product/Sku/Result/SkuItemResult.php | 60 ++++++ .../Catalog/Product/Sku/Result/SkuResult.php | 24 +++ .../Catalog/Product/Sku/Result/SkusResult.php | 34 ++++ .../Catalog/Product/Sku/Service/Sku.php | 171 ++++++++++++++++++ .../Catalog/Catalog/Service/CatalogTest.php | 5 +- .../Offer/Result/OfferItemResultTest.php | 124 +++++++++++++ .../Product/Offer/Service/OfferTest.php | 95 ++++++++++ .../Result/ProductServiceItemResultTest.php | 102 +++++++++++ .../Service/ProductServiceTest.php | 72 ++++++++ .../Catalog/Product/Service/ProductTest.php | 7 +- .../Product/Sku/Result/SkuItemResultTest.php | 113 ++++++++++++ .../Catalog/Product/Sku/Service/SkuTest.php | 72 ++++++++ .../Product/Offer/Service/OfferTest.php | 101 +++++++++++ .../Service/ProductServiceTest.php | 101 +++++++++++ .../Catalog/Product/Service/ProductTest.php | 55 ++++++ .../Catalog/Product/Sku/Service/SkuTest.php | 101 +++++++++++ 40 files changed, 2020 insertions(+), 40 deletions(-) create mode 100644 src/Services/Catalog/Product/Offer/Result/OfferItemResult.php create mode 100644 src/Services/Catalog/Product/Offer/Result/OfferResult.php create mode 100644 src/Services/Catalog/Product/Offer/Result/OffersResult.php create mode 100644 src/Services/Catalog/Product/Offer/Service/Offer.php create mode 100644 src/Services/Catalog/Product/ProductService/Result/ProductServiceItemResult.php create mode 100644 src/Services/Catalog/Product/ProductService/Result/ProductServiceResult.php create mode 100644 src/Services/Catalog/Product/ProductService/Result/ProductServicesResult.php create mode 100644 src/Services/Catalog/Product/ProductService/Service/ProductService.php create mode 100644 src/Services/Catalog/Product/Sku/Result/SkuItemResult.php create mode 100644 src/Services/Catalog/Product/Sku/Result/SkuResult.php create mode 100644 src/Services/Catalog/Product/Sku/Result/SkusResult.php create mode 100644 src/Services/Catalog/Product/Sku/Service/Sku.php create mode 100644 tests/Integration/Services/Catalog/Product/Offer/Result/OfferItemResultTest.php create mode 100644 tests/Integration/Services/Catalog/Product/Offer/Service/OfferTest.php create mode 100644 tests/Integration/Services/Catalog/Product/ProductService/Result/ProductServiceItemResultTest.php create mode 100644 tests/Integration/Services/Catalog/Product/ProductService/Service/ProductServiceTest.php create mode 100644 tests/Integration/Services/Catalog/Product/Sku/Result/SkuItemResultTest.php create mode 100644 tests/Integration/Services/Catalog/Product/Sku/Service/SkuTest.php create mode 100644 tests/Unit/Services/Catalog/Product/Offer/Service/OfferTest.php create mode 100644 tests/Unit/Services/Catalog/Product/ProductService/Service/ProductServiceTest.php create mode 100644 tests/Unit/Services/Catalog/Product/Service/ProductTest.php create mode 100644 tests/Unit/Services/Catalog/Product/Sku/Service/SkuTest.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..796ddf46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,40 @@ ### Added +- Added `update` and `download` methods to `Services\Catalog\Product\Service\Product` for + `catalog.product.update` / `catalog.product.download`, + see [catalog.product.* methods](https://apidocs.bitrix24.com/api-reference/catalog/product/index.html) ([#527](https://github.com/bitrix24/b24phpsdk/issues/527)) +- Added service `Services\Catalog\Product\ProductService\Service\ProductService` with support for + `catalog.product.service.*` methods, + see [catalog.product.service.* methods](https://apidocs.bitrix24.com/api-reference/catalog/product/service/index.html) ([#527](https://github.com/bitrix24/b24phpsdk/issues/527)): + - `add` creates a new service + - `update` updates an existing service + - `get` gets information about the service by its identifier + - `list` gets the list of services by filter + - `delete` deletes a service + - `fieldsByFilter` returns service field descriptions by iblock filter (`catalog.product.service.getFieldsByFilter`) + - `download` downloads a service file +- Added service `Services\Catalog\Product\Sku\Service\Sku` with support for `catalog.product.sku.*` + methods, + see [catalog.product.sku.* methods](https://apidocs.bitrix24.com/api-reference/catalog/product/sku/index.html) ([#527](https://github.com/bitrix24/b24phpsdk/issues/527)): + - `add` creates a new parent (SKU) product + - `update` updates an existing parent product + - `get` gets information about the parent product by its identifier + - `list` gets the list of parent products by filter + - `delete` deletes a parent product + - `fieldsByFilter` returns parent product field descriptions by iblock filter (`catalog.product.sku.getFieldsByFilter`) + - `download` downloads a parent product file +- Added service `Services\Catalog\Product\Offer\Service\Offer` with support for + `catalog.product.offer.*` methods, + see [catalog.product.offer.* methods](https://apidocs.bitrix24.com/api-reference/catalog/product/offer/index.html) ([#527](https://github.com/bitrix24/b24phpsdk/issues/527)): + - `add` creates a new product variation (offer) + - `update` updates an existing product variation + - `get` gets information about the product variation by its identifier + - `list` gets the list of product variations by filter + - `delete` deletes a product variation + - `fieldsByFilter` returns product variation field descriptions by iblock filter (`catalog.product.offer.getFieldsByFilter`) + - `download` downloads a product variation file + - 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 @@ -156,6 +190,11 @@ All classes under `Bitrix24\SDK\Legacy\` are marked `@deprecated` and will be removed once v3 reaches feature parity with v1. +### Fixed + +- Fixed `Services\Catalog\Catalog\Result\CatalogsResult::getCatalogs()` returning `ProductItemResult` + instances instead of `CatalogItemResult` ([#527](https://github.com/bitrix24/b24phpsdk/issues/527)) + ## 3.0.0 - 2026.01.01 ### Added diff --git a/Makefile b/Makefile index c2fc4a9a..d773c0a3 100644 --- a/Makefile +++ b/Makefile @@ -69,6 +69,12 @@ help: @echo "test-integration-landing-role - run Landing Role integration tests" @echo "test-integration-landing-repowidget - run Landing RepoWidget integration tests" @echo "test-integration-scope-landing-template - run Landing Template integration tests" + @echo "test-integration-scope-catalog - run Catalog scope integration tests" + @echo "test-integration-catalog-catalog - run Catalog Catalog integration tests" + @echo "test-integration-catalog-product - run Catalog Product integration tests" + @echo "test-integration-catalog-product-service - run Catalog ProductService integration tests" + @echo "test-integration-catalog-product-sku - run Catalog Product Sku integration tests" + @echo "test-integration-catalog-product-offer - run Catalog Product Offer integration tests" .PHONY: docker-init @@ -492,6 +498,30 @@ 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-scope-catalog +test-integration-scope-catalog: + docker compose run --rm php-cli $(PHPUNIT) --testsuite integration_tests_scope_catalog + +.PHONY: test-integration-catalog-catalog +test-integration-catalog-catalog: + docker compose run --rm php-cli $(PHPUNIT) --testsuite integration_tests_catalog_catalog + +.PHONY: test-integration-catalog-product +test-integration-catalog-product: + docker compose run --rm php-cli $(PHPUNIT) --testsuite integration_tests_catalog_product + +.PHONY: test-integration-catalog-product-service +test-integration-catalog-product-service: + docker compose run --rm php-cli $(PHPUNIT) --testsuite integration_tests_catalog_product_service + +.PHONY: test-integration-catalog-product-sku +test-integration-catalog-product-sku: + docker compose run --rm php-cli $(PHPUNIT) --testsuite integration_tests_catalog_product_sku + +.PHONY: test-integration-catalog-product-offer +test-integration-catalog-product-offer: + docker compose run --rm php-cli $(PHPUNIT) --testsuite integration_tests_catalog_product_offer + # work dev environment .PHONY: php-dev-server-up php-dev-server-up: diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d0187ae5..f51c2705 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -262,6 +262,27 @@ ./tests/Integration/Services/Landing/RepoWidget/ + + ./tests/Integration/Services/Catalog/ + + + ./tests/Integration/Services/Catalog/Catalog/Service/CatalogTest.php + + + ./tests/Integration/Services/Catalog/Product/Service/ProductTest.php + + + ./tests/Integration/Services/Catalog/Product/ProductService/Service/ProductServiceTest.php + ./tests/Integration/Services/Catalog/Product/ProductService/Result/ProductServiceItemResultTest.php + + + ./tests/Integration/Services/Catalog/Product/Sku/Service/SkuTest.php + ./tests/Integration/Services/Catalog/Product/Sku/Result/SkuItemResultTest.php + + + ./tests/Integration/Services/Catalog/Product/Offer/Service/OfferTest.php + ./tests/Integration/Services/Catalog/Product/Offer/Result/OfferItemResultTest.php + 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..1fb78b88 100644 --- a/src/Services/Catalog/Catalog/Result/CatalogsResult.php +++ b/src/Services/Catalog/Catalog/Result/CatalogsResult.php @@ -15,21 +15,20 @@ use Bitrix24\SDK\Core\Exceptions\BaseException; use Bitrix24\SDK\Core\Result\AbstractResult; -use Bitrix24\SDK\Services\Catalog\Product\Result\ProductItemResult; class CatalogsResult extends AbstractResult { /** - * @return ProductItemResult[] + * @return CatalogItemResult[] * @throws BaseException */ public function getCatalogs(): array { $res = []; - foreach ($this->getCoreResponse()->getResponseData()->getResult()['catalogs'] as $product) { - $res[] = new ProductItemResult($product); + foreach ($this->getCoreResponse()->getResponseData()->getResult()['catalogs'] as $catalog) { + $res[] = new CatalogItemResult($catalog); } 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..73be0c8d 100644 --- a/src/Services/Catalog/CatalogServiceBuilder.php +++ b/src/Services/Catalog/CatalogServiceBuilder.php @@ -17,9 +17,22 @@ use Bitrix24\SDK\Core\Credentials\Scope; use Bitrix24\SDK\Services\AbstractServiceBuilder; use Bitrix24\SDK\Services\Catalog; + #[ApiServiceBuilderMetadata(new Scope(['catalog']))] class CatalogServiceBuilder extends AbstractServiceBuilder { + public function catalog(): Catalog\Catalog\Service\Catalog + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Catalog\Catalog\Service\Catalog( + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } + public function product(): Catalog\Product\Service\Product { if (!isset($this->serviceCache[__METHOD__])) { @@ -33,10 +46,34 @@ public function product(): Catalog\Product\Service\Product return $this->serviceCache[__METHOD__]; } - public function catalog(): Catalog\Catalog\Service\Catalog + public function productService(): Catalog\Product\ProductService\Service\ProductService { if (!isset($this->serviceCache[__METHOD__])) { - $this->serviceCache[__METHOD__] = new Catalog\Catalog\Service\Catalog( + $this->serviceCache[__METHOD__] = new Catalog\Product\ProductService\Service\ProductService( + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } + + public function productSku(): Catalog\Product\Sku\Service\Sku + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Catalog\Product\Sku\Service\Sku( + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } + + public function productOffer(): Catalog\Product\Offer\Service\Offer + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Catalog\Product\Offer\Service\Offer( $this->core, $this->log ); @@ -44,4 +81,4 @@ public function catalog(): Catalog\Catalog\Service\Catalog return $this->serviceCache[__METHOD__]; } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Common/ProductType.php b/src/Services/Catalog/Common/ProductType.php index 0bf8ef09..6b3b8428 100644 --- a/src/Services/Catalog/Common/ProductType.php +++ b/src/Services/Catalog/Common/ProductType.php @@ -20,4 +20,6 @@ enum ProductType: int case SKU = 3; case productOffer = 4; case genericOffer = 5; -} \ No newline at end of file + case skuMaster = 6; + case service = 7; +} diff --git a/src/Services/Catalog/Common/Result/AbstractCatalogItem.php b/src/Services/Catalog/Common/Result/AbstractCatalogItem.php index e2929d73..822df379 100644 --- a/src/Services/Catalog/Common/Result/AbstractCatalogItem.php +++ b/src/Services/Catalog/Common/Result/AbstractCatalogItem.php @@ -39,12 +39,14 @@ public function __construct(array $data, ?Currency $currency = null) * @return bool|CarbonImmutable|int|mixed|null */ + #[\Override] public function __get($offset) { switch ($offset) { case 'active': case 'available': case 'bundle': + case 'vatIncluded': return $this->data[$offset] === 'Y'; case 'barcodeMulti': case 'canBuyZero': @@ -69,6 +71,7 @@ public function __get($offset) case 'sort': case 'height': case 'length': + case 'vatId': if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { return (int)$this->data[$offset]; } @@ -78,7 +81,7 @@ public function __get($offset) case 'dateActiveTo': case 'dateCreate': case 'timestampX': - if ($this->data[$offset] !== '') { + if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { return CarbonImmutable::createFromFormat(DATE_ATOM, $this->data[$offset]); } @@ -109,4 +112,4 @@ protected function getKeyWithUserfieldByFieldName(string $fieldName) return $this->$fieldName; } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Product/Offer/Result/OfferItemResult.php b/src/Services/Catalog/Product/Offer/Result/OfferItemResult.php new file mode 100644 index 00000000..70d05421 --- /dev/null +++ b/src/Services/Catalog/Product/Offer/Result/OfferItemResult.php @@ -0,0 +1,68 @@ + + * + * 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\Product\Offer\Result; + +use Bitrix24\SDK\Services\Catalog\Common\ProductType; +use Bitrix24\SDK\Services\Catalog\Common\Result\AbstractCatalogItem; +use Carbon\CarbonImmutable; + +/** + * @property-read bool $active + * @property-read bool $available + * @property-read ?bool $barcodeMulti + * @property-read bool $bundle + * @property-read ?bool $canBuyZero + * @property-read string $code + * @property-read int $createdBy + * @property-read CarbonImmutable|null $dateActiveFrom + * @property-read CarbonImmutable|null $dateActiveTo + * @property-read CarbonImmutable $dateCreate + * @property-read array|null $detailPicture + * @property-read string $detailText + * @property-read string $detailTextType + * @property-read ?int $height + * @property-read int $id + * @property-read int $iblockId + * @property-read ?array $iblockSection + * @property-read ?int $iblockSectionId + * @property-read ?int $length + * @property-read ?int $measure + * @property-read int $modifiedBy + * @property-read string $name + * @property-read ?array $parentId + * @property-read array|null $previewPicture + * @property-read string $previewText + * @property-read string $previewTextType + * @property-read ?string $purchasingCurrency + * @property-read ?string $purchasingPrice + * @property-read ?int $quantity + * @property-read ?int $quantityReserved + * @property-read string $quantityTrace + * @property-read ?int $recurSchemeLength + * @property-read ?string $recurSchemeType + * @property-read int $sort + * @property-read string $subscribe + * @property-read CarbonImmutable $timestampX + * @property-read ?int $trialPriceId + * @property-read ProductType $type + * @property-read ?int $vatId + * @property-read bool $vatIncluded + * @property-read ?int $weight + * @property-read ?int $width + * @property-read string $withoutOrder + * @property-read string $xmlId + */ +class OfferItemResult extends AbstractCatalogItem +{ +} diff --git a/src/Services/Catalog/Product/Offer/Result/OfferResult.php b/src/Services/Catalog/Product/Offer/Result/OfferResult.php new file mode 100644 index 00000000..2e7a7455 --- /dev/null +++ b/src/Services/Catalog/Product/Offer/Result/OfferResult.php @@ -0,0 +1,24 @@ + + * + * 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\Product\Offer\Result; + +use Bitrix24\SDK\Core\Result\AbstractResult; + +class OfferResult extends AbstractResult +{ + public function offer(): OfferItemResult + { + return new OfferItemResult($this->getCoreResponse()->getResponseData()->getResult()['offer']); + } +} diff --git a/src/Services/Catalog/Product/Offer/Result/OffersResult.php b/src/Services/Catalog/Product/Offer/Result/OffersResult.php new file mode 100644 index 00000000..4e657953 --- /dev/null +++ b/src/Services/Catalog/Product/Offer/Result/OffersResult.php @@ -0,0 +1,34 @@ + + * + * 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\Product\Offer\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class OffersResult extends AbstractResult +{ + /** + * @return OfferItemResult[] + * @throws BaseException + */ + public function getOffers(): array + { + $res = []; + foreach ($this->getCoreResponse()->getResponseData()->getResult()['offers'] as $offer) { + $res[] = new OfferItemResult($offer); + } + + return $res; + } +} diff --git a/src/Services/Catalog/Product/Offer/Service/Offer.php b/src/Services/Catalog/Product/Offer/Service/Offer.php new file mode 100644 index 00000000..b1feb835 --- /dev/null +++ b/src/Services/Catalog/Product/Offer/Service/Offer.php @@ -0,0 +1,171 @@ + + * + * 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\Product\Offer\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\TransportException; +use Bitrix24\SDK\Core\Response\Response; +use Bitrix24\SDK\Core\Result\DeletedItemResult; +use Bitrix24\SDK\Core\Result\FieldsResult; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\Catalog\Product\Offer\Result\OfferResult; +use Bitrix24\SDK\Services\Catalog\Product\Offer\Result\OffersResult; +use Psr\Log\LoggerInterface; + +#[ApiServiceMetadata(new Scope(['catalog']))] +class Offer extends AbstractService +{ + public function __construct(CoreInterface $core, LoggerInterface $logger) + { + parent::__construct($core, $logger); + } + + /** + * The method adds a product variation (offer) to the commercial catalog. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/offer/catalog-product-offer-add.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.offer.add', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/offer/catalog-product-offer-add.html', + 'The method adds a product variation (offer) to the commercial catalog.' + )] + public function add(array $fields): OfferResult + { + return new OfferResult($this->core->call('catalog.product.offer.add', ['fields' => $fields])); + } + + /** + * The method updates a product variation (offer) in the commercial catalog by its identifier. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/offer/catalog-product-offer-update.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.offer.update', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/offer/catalog-product-offer-update.html', + 'The method updates a product variation (offer) in the commercial catalog by its identifier.' + )] + public function update(int $offerId, array $fields): OfferResult + { + return new OfferResult($this->core->call('catalog.product.offer.update', [ + 'id' => $offerId, + 'fields' => $fields, + ])); + } + + /** + * The method gets field values of a product variation (offer) by ID. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/offer/catalog-product-offer-get.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.offer.get', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/offer/catalog-product-offer-get.html', + 'The method gets field values of a product variation (offer) by ID.' + )] + public function get(int $offerId): OfferResult + { + return new OfferResult($this->core->call('catalog.product.offer.get', ['id' => $offerId])); + } + + /** + * The method gets a list of product variations (offers) by filter. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/offer/catalog-product-offer-list.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.offer.list', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/offer/catalog-product-offer-list.html', + 'The method gets a list of product variations (offers) by filter.' + )] + public function list(array $select, array $filter, array $order = []): OffersResult + { + return new OffersResult($this->core->call('catalog.product.offer.list', [ + 'select' => $select, + 'filter' => $filter, + 'order' => $order, + ])); + } + + /** + * The method deletes a product variation (offer) by ID. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/offer/catalog-product-offer-delete.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.offer.delete', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/offer/catalog-product-offer-delete.html', + 'The method deletes a product variation (offer) by ID.' + )] + public function delete(int $offerId): DeletedItemResult + { + return new DeletedItemResult($this->core->call('catalog.product.offer.delete', ['id' => $offerId])); + } + + /** + * The method returns product variation (offer) fields by filter. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/offer/catalog-product-offer-get-fields-by-filter.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.offer.getFieldsByFilter', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/offer/catalog-product-offer-get-fields-by-filter.html', + 'The method returns product variation (offer) fields by filter.' + )] + public function fieldsByFilter(int $iblockId): FieldsResult + { + return new FieldsResult($this->core->call('catalog.product.offer.getFieldsByFilter', [ + 'filter' => ['iblockId' => $iblockId], + ])); + } + + /** + * The method downloads product variation (offer) files by the given parameters. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/offer/catalog-product-offer-download.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.offer.download', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/offer/catalog-product-offer-download.html', + 'The method downloads product variation (offer) files by the given parameters.' + )] + public function download(int $fileId, int $productId, string $fieldName): Response + { + return $this->core->call('catalog.product.offer.download', [ + 'fields' => [ + 'fileId' => $fileId, + 'productId' => $productId, + 'fieldName' => $fieldName, + ], + ]); + } +} diff --git a/src/Services/Catalog/Product/ProductService/Result/ProductServiceItemResult.php b/src/Services/Catalog/Product/ProductService/Result/ProductServiceItemResult.php new file mode 100644 index 00000000..e33bd76b --- /dev/null +++ b/src/Services/Catalog/Product/ProductService/Result/ProductServiceItemResult.php @@ -0,0 +1,51 @@ + + * + * 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\Product\ProductService\Result; + +use Bitrix24\SDK\Services\Catalog\Common\ProductType; +use Bitrix24\SDK\Services\Catalog\Common\Result\AbstractCatalogItem; +use Carbon\CarbonImmutable; + +/** + * @property-read bool $active + * @property-read bool $available + * @property-read bool $bundle + * @property-read string $code + * @property-read int $createdBy + * @property-read CarbonImmutable|null $dateActiveFrom + * @property-read CarbonImmutable|null $dateActiveTo + * @property-read CarbonImmutable $dateCreate + * @property-read array|null $detailPicture + * @property-read string $detailText + * @property-read string $detailTextType + * @property-read int $id + * @property-read int $iblockId + * @property-read ?array $iblockSection + * @property-read ?int $iblockSectionId + * @property-read ?int $measure + * @property-read int $modifiedBy + * @property-read string $name + * @property-read array|null $previewPicture + * @property-read string $previewText + * @property-read string $previewTextType + * @property-read int $sort + * @property-read CarbonImmutable $timestampX + * @property-read ProductType $type + * @property-read ?int $vatId + * @property-read bool $vatIncluded + * @property-read string $xmlId + */ +class ProductServiceItemResult extends AbstractCatalogItem +{ +} diff --git a/src/Services/Catalog/Product/ProductService/Result/ProductServiceResult.php b/src/Services/Catalog/Product/ProductService/Result/ProductServiceResult.php new file mode 100644 index 00000000..caf43c27 --- /dev/null +++ b/src/Services/Catalog/Product/ProductService/Result/ProductServiceResult.php @@ -0,0 +1,24 @@ + + * + * 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\Product\ProductService\Result; + +use Bitrix24\SDK\Core\Result\AbstractResult; + +class ProductServiceResult extends AbstractResult +{ + public function productService(): ProductServiceItemResult + { + return new ProductServiceItemResult($this->getCoreResponse()->getResponseData()->getResult()['service']); + } +} diff --git a/src/Services/Catalog/Product/ProductService/Result/ProductServicesResult.php b/src/Services/Catalog/Product/ProductService/Result/ProductServicesResult.php new file mode 100644 index 00000000..7ce9b208 --- /dev/null +++ b/src/Services/Catalog/Product/ProductService/Result/ProductServicesResult.php @@ -0,0 +1,34 @@ + + * + * 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\Product\ProductService\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class ProductServicesResult extends AbstractResult +{ + /** + * @return ProductServiceItemResult[] + * @throws BaseException + */ + public function getProductServices(): array + { + $res = []; + foreach ($this->getCoreResponse()->getResponseData()->getResult()['services'] as $service) { + $res[] = new ProductServiceItemResult($service); + } + + return $res; + } +} diff --git a/src/Services/Catalog/Product/ProductService/Service/ProductService.php b/src/Services/Catalog/Product/ProductService/Service/ProductService.php new file mode 100644 index 00000000..662a7836 --- /dev/null +++ b/src/Services/Catalog/Product/ProductService/Service/ProductService.php @@ -0,0 +1,171 @@ + + * + * 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\Product\ProductService\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\TransportException; +use Bitrix24\SDK\Core\Response\Response; +use Bitrix24\SDK\Core\Result\DeletedItemResult; +use Bitrix24\SDK\Core\Result\FieldsResult; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\Catalog\Product\ProductService\Result\ProductServiceResult; +use Bitrix24\SDK\Services\Catalog\Product\ProductService\Result\ProductServicesResult; +use Psr\Log\LoggerInterface; + +#[ApiServiceMetadata(new Scope(['catalog']))] +class ProductService extends AbstractService +{ + public function __construct(CoreInterface $core, LoggerInterface $logger) + { + parent::__construct($core, $logger); + } + + /** + * The method adds a service to the commercial catalog. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/service/catalog-product-service-add.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.service.add', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/service/catalog-product-service-add.html', + 'The method adds a service to the commercial catalog.' + )] + public function add(array $fields): ProductServiceResult + { + return new ProductServiceResult($this->core->call('catalog.product.service.add', ['fields' => $fields])); + } + + /** + * The method updates a service in the commercial catalog by its identifier. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/service/catalog-product-service-update.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.service.update', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/service/catalog-product-service-update.html', + 'The method updates a service in the commercial catalog by its identifier.' + )] + public function update(int $serviceId, array $fields): ProductServiceResult + { + return new ProductServiceResult($this->core->call('catalog.product.service.update', [ + 'id' => $serviceId, + 'fields' => $fields, + ])); + } + + /** + * The method gets field values of a commercial catalog service by ID. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/service/catalog-product-service-get.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.service.get', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/service/catalog-product-service-get.html', + 'The method gets field values of a commercial catalog service by ID.' + )] + public function get(int $serviceId): ProductServiceResult + { + return new ProductServiceResult($this->core->call('catalog.product.service.get', ['id' => $serviceId])); + } + + /** + * The method gets a list of commercial catalog services by filter. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/service/catalog-product-service-list.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.service.list', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/service/catalog-product-service-list.html', + 'The method gets a list of commercial catalog services by filter.' + )] + public function list(array $select, array $filter, array $order = []): ProductServicesResult + { + return new ProductServicesResult($this->core->call('catalog.product.service.list', [ + 'select' => $select, + 'filter' => $filter, + 'order' => $order, + ])); + } + + /** + * The method deletes a commercial catalog service by ID. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/service/catalog-product-service-delete.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.service.delete', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/service/catalog-product-service-delete.html', + 'The method deletes a commercial catalog service by ID.' + )] + public function delete(int $serviceId): DeletedItemResult + { + return new DeletedItemResult($this->core->call('catalog.product.service.delete', ['id' => $serviceId])); + } + + /** + * The method returns commercial catalog service fields by filter. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/service/catalog-product-service-get-fields-by-filter.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.service.getFieldsByFilter', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/service/catalog-product-service-get-fields-by-filter.html', + 'The method returns commercial catalog service fields by filter.' + )] + public function fieldsByFilter(int $iblockId): FieldsResult + { + return new FieldsResult($this->core->call('catalog.product.service.getFieldsByFilter', [ + 'filter' => ['iblockId' => $iblockId], + ])); + } + + /** + * The method downloads commercial catalog service files by the given parameters. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/service/catalog-product-service-download.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.service.download', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/service/catalog-product-service-download.html', + 'The method downloads commercial catalog service files by the given parameters.' + )] + public function download(int $fileId, int $productId, string $fieldName): Response + { + return $this->core->call('catalog.product.service.download', [ + 'fields' => [ + 'fileId' => $fileId, + 'productId' => $productId, + 'fieldName' => $fieldName, + ], + ]); + } +} 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..a0df5172 100644 --- a/src/Services/Catalog/Product/Service/Product.php +++ b/src/Services/Catalog/Product/Service/Product.php @@ -19,13 +19,13 @@ use Bitrix24\SDK\Core\Credentials\Scope; use Bitrix24\SDK\Core\Exceptions\BaseException; use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Core\Response\Response; use Bitrix24\SDK\Core\Result\DeletedItemResult; use Bitrix24\SDK\Core\Result\FieldsResult; use Bitrix24\SDK\Services\AbstractService; 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,21 +35,20 @@ public function __construct( public Batch $batch, CoreInterface $core, LoggerInterface $logger - ) - { + ) { parent::__construct($core, $logger); } /** * The method gets field value of commercial catalog product by ID. * - * @see https://training.bitrix24.com/rest_help/catalog/product/catalog_product_get.php + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/catalog-product-get.html * @throws TransportException * @throws BaseException */ #[ApiEndpointMetadata( 'catalog.product.get', - 'https://training.bitrix24.com/rest_help/catalog/product/catalog_product_get.php', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/catalog-product-get.html', 'The method gets field value of commercial catalog product by ID.' )] public function get(int $productId): ProductResult @@ -60,18 +59,20 @@ public function get(int $productId): ProductResult /** * The method adds a commercial catalog product. * - * @see https://training.bitrix24.com/rest_help/catalog/product/catalog_product_add.php + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/catalog-product-add.html * @throws BaseException * @throws TransportException */ #[ApiEndpointMetadata( 'catalog.product.add', - 'https://training.bitrix24.com/rest_help/catalog/product/catalog_product_add.php', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/catalog-product-add.html', 'The method adds a commercial catalog product.' )] 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 ] )); @@ -80,13 +81,13 @@ public function add(array $productFields): ProductResult /** * The method deletes commercial catalog product. * - * @see https://training.bitrix24.com/rest_help/catalog/product/catalog_product_delete.php + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/catalog-product-delete.html * @throws BaseException * @throws TransportException */ #[ApiEndpointMetadata( 'catalog.product.delete', - 'https://training.bitrix24.com/rest_help/catalog/product/catalog_product_delete.php', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/catalog-product-delete.html', 'The method deletes commercial catalog product by ID' )] public function delete(int $productId): DeletedItemResult @@ -97,13 +98,13 @@ public function delete(int $productId): DeletedItemResult /** * The method gets list of commercial catalog products by filter. * - * @see https://training.bitrix24.com/rest_help/catalog/product/catalog_product_list.php + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/catalog-product-list.html * @throws TransportException * @throws BaseException */ #[ApiEndpointMetadata( 'catalog.product.list', - 'https://training.bitrix24.com/rest_help/catalog/product/catalog_product_list.php', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/catalog-product-list.html', 'The method gets list of commercial catalog products by filter.' )] public function list(array $select, array $filter, array $order, int $start): ProductsResult @@ -118,14 +119,14 @@ public function list(array $select, array $filter, array $order, int $start): Pr /** * The method returns commercial catalog product fields by filter. - * @see https://training.bitrix24.com/rest_help/catalog/product/catalog_product_getfieldsbyfilter.php + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/catalog-product-get-fields-by-filter.html * * @throws BaseException * @throws TransportException */ #[ApiEndpointMetadata( 'catalog.product.getFieldsByFilter', - 'https://training.bitrix24.com/rest_help/catalog/product/catalog_product_getfieldsbyfilter.php', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/catalog-product-get-fields-by-filter.html', 'The method returns commercial catalog product fields by filter.' )] public function fieldsByFilter(int $iblockId, ProductType $productType, ?array $additionalFilter = null): FieldsResult @@ -140,4 +141,47 @@ 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 + + /** + * The method updates commercial catalog product by ID with the given fields. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/catalog-product-update.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.update', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/catalog-product-update.html', + 'The method updates commercial catalog product by ID with the given fields.' + )] + public function update(int $productId, array $productFields): ProductResult + { + return new ProductResult($this->core->call('catalog.product.update', [ + 'id' => $productId, + 'fields' => $productFields, + ])); + } + + /** + * The method downloads commercial catalog product files by the given parameters. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/catalog-product-download.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.download', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/catalog-product-download.html', + 'The method downloads commercial catalog product files by the given parameters.' + )] + public function download(int $fileId, int $productId, string $fieldName): Response + { + return $this->core->call('catalog.product.download', [ + 'fields' => [ + 'fileId' => $fileId, + 'productId' => $productId, + 'fieldName' => $fieldName, + ], + ]); + } +} diff --git a/src/Services/Catalog/Product/Sku/Result/SkuItemResult.php b/src/Services/Catalog/Product/Sku/Result/SkuItemResult.php new file mode 100644 index 00000000..7df55bc5 --- /dev/null +++ b/src/Services/Catalog/Product/Sku/Result/SkuItemResult.php @@ -0,0 +1,60 @@ + + * + * 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\Product\Sku\Result; + +use Bitrix24\SDK\Services\Catalog\Common\ProductType; +use Bitrix24\SDK\Services\Catalog\Common\Result\AbstractCatalogItem; +use Carbon\CarbonImmutable; + +/** + * @property-read bool $active + * @property-read bool $available + * @property-read bool $bundle + * @property-read ?bool $canBuyZero + * @property-read string $code + * @property-read int $createdBy + * @property-read CarbonImmutable|null $dateActiveFrom + * @property-read CarbonImmutable|null $dateActiveTo + * @property-read CarbonImmutable $dateCreate + * @property-read array|null $detailPicture + * @property-read string $detailText + * @property-read string $detailTextType + * @property-read ?int $height + * @property-read int $id + * @property-read int $iblockId + * @property-read ?array $iblockSection + * @property-read ?int $iblockSectionId + * @property-read ?int $length + * @property-read ?int $measure + * @property-read int $modifiedBy + * @property-read string $name + * @property-read array|null $previewPicture + * @property-read string $previewText + * @property-read string $previewTextType + * @property-read ?string $purchasingCurrency + * @property-read ?string $purchasingPrice + * @property-read ?int $quantity + * @property-read int $sort + * @property-read string $subscribe + * @property-read CarbonImmutable $timestampX + * @property-read ProductType $type + * @property-read ?int $vatId + * @property-read bool $vatIncluded + * @property-read ?int $weight + * @property-read ?int $width + * @property-read string $xmlId + */ +class SkuItemResult extends AbstractCatalogItem +{ +} diff --git a/src/Services/Catalog/Product/Sku/Result/SkuResult.php b/src/Services/Catalog/Product/Sku/Result/SkuResult.php new file mode 100644 index 00000000..5257292e --- /dev/null +++ b/src/Services/Catalog/Product/Sku/Result/SkuResult.php @@ -0,0 +1,24 @@ + + * + * 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\Product\Sku\Result; + +use Bitrix24\SDK\Core\Result\AbstractResult; + +class SkuResult extends AbstractResult +{ + public function sku(): SkuItemResult + { + return new SkuItemResult($this->getCoreResponse()->getResponseData()->getResult()['sku']); + } +} diff --git a/src/Services/Catalog/Product/Sku/Result/SkusResult.php b/src/Services/Catalog/Product/Sku/Result/SkusResult.php new file mode 100644 index 00000000..2176fc6f --- /dev/null +++ b/src/Services/Catalog/Product/Sku/Result/SkusResult.php @@ -0,0 +1,34 @@ + + * + * 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\Product\Sku\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class SkusResult extends AbstractResult +{ + /** + * @return SkuItemResult[] + * @throws BaseException + */ + public function getSkus(): array + { + $res = []; + foreach ($this->getCoreResponse()->getResponseData()->getResult()['units'] as $unit) { + $res[] = new SkuItemResult($unit); + } + + return $res; + } +} diff --git a/src/Services/Catalog/Product/Sku/Service/Sku.php b/src/Services/Catalog/Product/Sku/Service/Sku.php new file mode 100644 index 00000000..09f80d4d --- /dev/null +++ b/src/Services/Catalog/Product/Sku/Service/Sku.php @@ -0,0 +1,171 @@ + + * + * 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\Product\Sku\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\TransportException; +use Bitrix24\SDK\Core\Response\Response; +use Bitrix24\SDK\Core\Result\DeletedItemResult; +use Bitrix24\SDK\Core\Result\FieldsResult; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\Catalog\Product\Sku\Result\SkuResult; +use Bitrix24\SDK\Services\Catalog\Product\Sku\Result\SkusResult; +use Psr\Log\LoggerInterface; + +#[ApiServiceMetadata(new Scope(['catalog']))] +class Sku extends AbstractService +{ + public function __construct(CoreInterface $core, LoggerInterface $logger) + { + parent::__construct($core, $logger); + } + + /** + * The method adds a parent (SKU) product to the commercial catalog. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/sku/catalog-product-sku-add.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.sku.add', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/sku/catalog-product-sku-add.html', + 'The method adds a parent (SKU) product to the commercial catalog.' + )] + public function add(array $fields): SkuResult + { + return new SkuResult($this->core->call('catalog.product.sku.add', ['fields' => $fields])); + } + + /** + * The method updates a parent (SKU) product in the commercial catalog by its identifier. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/sku/catalog-product-sku-update.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.sku.update', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/sku/catalog-product-sku-update.html', + 'The method updates a parent (SKU) product in the commercial catalog by its identifier.' + )] + public function update(int $skuId, array $fields): SkuResult + { + return new SkuResult($this->core->call('catalog.product.sku.update', [ + 'id' => $skuId, + 'fields' => $fields, + ])); + } + + /** + * The method gets field values of a parent (SKU) product by ID. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/sku/catalog-product-sku-get.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.sku.get', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/sku/catalog-product-sku-get.html', + 'The method gets field values of a parent (SKU) product by ID.' + )] + public function get(int $skuId): SkuResult + { + return new SkuResult($this->core->call('catalog.product.sku.get', ['id' => $skuId])); + } + + /** + * The method gets a list of parent (SKU) products by filter. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/sku/catalog-product-sku-list.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.sku.list', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/sku/catalog-product-sku-list.html', + 'The method gets a list of parent (SKU) products by filter.' + )] + public function list(array $select, array $filter, array $order = []): SkusResult + { + return new SkusResult($this->core->call('catalog.product.sku.list', [ + 'select' => $select, + 'filter' => $filter, + 'order' => $order, + ])); + } + + /** + * The method deletes a parent (SKU) product by ID. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/sku/catalog-product-sku-delete.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.sku.delete', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/sku/catalog-product-sku-delete.html', + 'The method deletes a parent (SKU) product by ID.' + )] + public function delete(int $skuId): DeletedItemResult + { + return new DeletedItemResult($this->core->call('catalog.product.sku.delete', ['id' => $skuId])); + } + + /** + * The method returns parent (SKU) product fields by filter. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/sku/catalog-product-sku-get-fields-by-filter.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.sku.getFieldsByFilter', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/sku/catalog-product-sku-get-fields-by-filter.html', + 'The method returns parent (SKU) product fields by filter.' + )] + public function fieldsByFilter(int $iblockId): FieldsResult + { + return new FieldsResult($this->core->call('catalog.product.sku.getFieldsByFilter', [ + 'filter' => ['iblockId' => $iblockId], + ])); + } + + /** + * The method downloads parent (SKU) product files by the given parameters. + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/product/sku/catalog-product-sku-download.html + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.product.sku.download', + 'https://apidocs.bitrix24.com/api-reference/catalog/product/sku/catalog-product-sku-download.html', + 'The method downloads parent (SKU) product files by the given parameters.' + )] + public function download(int $fileId, int $productId, string $fieldName): Response + { + return $this->core->call('catalog.product.sku.download', [ + 'fields' => [ + 'fileId' => $fileId, + 'productId' => $productId, + 'fieldName' => $fieldName, + ], + ]); + } +} diff --git a/tests/Integration/Services/Catalog/Catalog/Service/CatalogTest.php b/tests/Integration/Services/Catalog/Catalog/Service/CatalogTest.php index 47e03abb..cedf54fa 100644 --- a/tests/Integration/Services/Catalog/Catalog/Service/CatalogTest.php +++ b/tests/Integration/Services/Catalog/Catalog/Service/CatalogTest.php @@ -16,7 +16,7 @@ use Bitrix24\SDK\Core\Exceptions\BaseException; use Bitrix24\SDK\Core\Exceptions\TransportException; use Bitrix24\SDK\Services\Catalog\Catalog\Service\Catalog; -use Bitrix24\SDK\Tests\Integration\Fabric; +use Bitrix24\SDK\Tests\Integration\Factory; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\TestCase; @@ -57,8 +57,9 @@ public function testGet(): void $this->assertEquals($catalog->id, $this->service->get($catalog->id)->catalog()->id); } + #[\Override] protected function setUp(): void { - $this->service = Fabric::getServiceBuilder()->getCatalogScope()->catalog(); + $this->service = Factory::getServiceBuilder()->getCatalogScope()->catalog(); } } \ No newline at end of file diff --git a/tests/Integration/Services/Catalog/Product/Offer/Result/OfferItemResultTest.php b/tests/Integration/Services/Catalog/Product/Offer/Result/OfferItemResultTest.php new file mode 100644 index 00000000..ab874da7 --- /dev/null +++ b/tests/Integration/Services/Catalog/Product/Offer/Result/OfferItemResultTest.php @@ -0,0 +1,124 @@ + + * + * 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\Product\Offer\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Catalog\Catalog\Service\Catalog; +use Bitrix24\SDK\Services\Catalog\Product\Offer\Result\OfferItemResult; +use Bitrix24\SDK\Services\Catalog\Product\Offer\Service\Offer; +use Bitrix24\SDK\Services\Catalog\Product\Sku\Service\Sku; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(OfferItemResult::class)] +class OfferItemResultTest extends TestCase +{ + use CustomBitrix24Assertions; + + private Offer $offerService; + + private Sku $skuService; + + private int $skuId; + + private int $offerId; + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function setUp(): void + { + $serviceBuilder = Factory::getServiceBuilder(); + $this->offerService = $serviceBuilder->getCatalogScope()->productOffer(); + $this->skuService = $serviceBuilder->getCatalogScope()->productSku(); + $catalogService = $serviceBuilder->getCatalogScope()->catalog(); + + $productCatalog = null; + $offersCatalog = null; + foreach ($catalogService->list([], [], [], 1)->getCatalogs() as $catalog) { + if ($catalog->productIblockId === null) { + $productCatalog = $catalog; + } else { + $offersCatalog = $catalog; + } + } + + $this->skuId = $this->skuService->add([ + 'iblockId' => $productCatalog->iblockId, + 'name' => sprintf('test sku for offer annotations %s', time()), + ])->sku()->id; + + $this->offerId = $this->offerService->add([ + 'iblockId' => $offersCatalog->iblockId, + 'name' => sprintf('test offer annotations %s', time()), + 'parentId' => $this->skuId, + ])->offer()->id; + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function tearDown(): void + { + $this->offerService->delete($this->offerId); + $this->skuService->delete($this->skuId); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[Test] + #[TestDox('all fields in OfferItemResult are annotated in phpdoc and match with raw api response')] + public function testAllFieldsAreAnnotated(): void + { + $rawItem = $this->offerService->get($this->offerId)->getCoreResponse() + ->getResponseData()->getResult()['offer']; + + // dynamic catalog properties (propertyN) vary per portal and are intentionally not annotated + $fieldCodes = array_filter( + array_keys($rawItem), + static fn (string $fieldCode): bool => !preg_match('/^property\d+$/', $fieldCode) + ); + + $this->assertBitrix24AllResultItemFieldsAnnotated( + $fieldCodes, + OfferItemResult::class + ); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[Test] + #[TestDox('all fields in OfferItemResult have valid type casting in magic getters')] + public function testAllFieldsHasValidTypeCastingInMagicGetters(): void + { + $item = $this->offerService->get($this->offerId)->offer(); + $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations( + $item, + OfferItemResult::class + ); + } +} diff --git a/tests/Integration/Services/Catalog/Product/Offer/Service/OfferTest.php b/tests/Integration/Services/Catalog/Product/Offer/Service/OfferTest.php new file mode 100644 index 00000000..91288761 --- /dev/null +++ b/tests/Integration/Services/Catalog/Product/Offer/Service/OfferTest.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\Integration\Services\Catalog\Product\Offer\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Catalog\Catalog\Service\Catalog; +use Bitrix24\SDK\Services\Catalog\Product\Offer\Service\Offer; +use Bitrix24\SDK\Services\Catalog\Product\Sku\Service\Sku; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Offer::class)] +class OfferTest extends TestCase +{ + private Offer $offerService; + + private Sku $skuService; + + private Catalog $catalogService; + + #[\Override] + protected function setUp(): void + { + $this->offerService = Factory::getServiceBuilder()->getCatalogScope()->productOffer(); + $this->skuService = Factory::getServiceBuilder()->getCatalogScope()->productSku(); + $this->catalogService = Factory::getServiceBuilder()->getCatalogScope()->catalog(); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[TestDox('test Offer::add, get, update, delete, list, fieldsByFilter')] + public function testAddGetUpdateDeleteListFieldsByFilter(): void + { + $catalogs = $this->catalogService->list([], [], [], 1)->getCatalogs(); + $productCatalog = null; + $offersCatalog = null; + foreach ($catalogs as $catalog) { + if ($catalog->productIblockId === null) { + $productCatalog = $catalog; + } else { + $offersCatalog = $catalog; + } + } + + $this->assertNotNull($productCatalog, 'products catalog not found'); + $this->assertNotNull($offersCatalog, 'offers catalog not found'); + + $skuId = $this->skuService->add([ + 'iblockId' => $productCatalog->iblockId, + 'name' => sprintf('test sku for offer %s', time()), + ])->sku()->id; + + $addResult = $this->offerService->add([ + 'iblockId' => $offersCatalog->iblockId, + 'name' => sprintf('test offer %s', time()), + 'parentId' => $skuId, + ]); + $offerId = $addResult->offer()->id; + $this->assertGreaterThan(0, $offerId); + + $getResult = $this->offerService->get($offerId); + $this->assertEquals($offerId, $getResult->offer()->id); + + $updated = $this->offerService->update($offerId, ['name' => 'updated offer name']); + $this->assertEquals('updated offer name', $updated->offer()->name); + + $listResult = $this->offerService->list( + ['id', 'iblockId'], + ['id' => $offerId, 'iblockId' => $offersCatalog->iblockId] + ); + $this->assertCount(1, $listResult->getOffers()); + + $fields = $this->offerService->fieldsByFilter($offersCatalog->iblockId); + $this->assertIsArray($fields->getFieldsDescription()); + + $this->assertTrue($this->offerService->delete($offerId)->isSuccess()); + $this->assertTrue($this->skuService->delete($skuId)->isSuccess()); + } +} diff --git a/tests/Integration/Services/Catalog/Product/ProductService/Result/ProductServiceItemResultTest.php b/tests/Integration/Services/Catalog/Product/ProductService/Result/ProductServiceItemResultTest.php new file mode 100644 index 00000000..fcc4f3c3 --- /dev/null +++ b/tests/Integration/Services/Catalog/Product/ProductService/Result/ProductServiceItemResultTest.php @@ -0,0 +1,102 @@ + + * + * 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\Product\ProductService\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Catalog\Catalog\Service\Catalog; +use Bitrix24\SDK\Services\Catalog\Product\ProductService\Result\ProductServiceItemResult; +use Bitrix24\SDK\Services\Catalog\Product\ProductService\Service\ProductService; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(ProductServiceItemResult::class)] +class ProductServiceItemResultTest extends TestCase +{ + use CustomBitrix24Assertions; + + private ProductService $productServiceScope; + + private int $serviceId; + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function setUp(): void + { + $serviceBuilder = Factory::getServiceBuilder(); + $this->productServiceScope = $serviceBuilder->getCatalogScope()->productService(); + $catalogService = $serviceBuilder->getCatalogScope()->catalog(); + $iblockId = $catalogService->list([], [], [], 1)->getCatalogs()[0]->iblockId; + + $this->serviceId = $this->productServiceScope->add([ + 'iblockId' => $iblockId, + 'name' => sprintf('test service annotations %s', time()), + ])->productService()->id; + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function tearDown(): void + { + $this->productServiceScope->delete($this->serviceId); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[Test] + #[TestDox('all fields in ProductServiceItemResult are annotated in phpdoc and match with raw api response')] + public function testAllFieldsAreAnnotated(): void + { + $rawItem = $this->productServiceScope->get($this->serviceId)->getCoreResponse() + ->getResponseData()->getResult()['service']; + + // dynamic catalog properties (propertyN) vary per portal and are intentionally not annotated + $fieldCodes = array_filter( + array_keys($rawItem), + static fn (string $fieldCode): bool => !preg_match('/^property\d+$/', $fieldCode) + ); + + $this->assertBitrix24AllResultItemFieldsAnnotated( + $fieldCodes, + ProductServiceItemResult::class + ); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[Test] + #[TestDox('all fields in ProductServiceItemResult have valid type casting in magic getters')] + public function testAllFieldsHasValidTypeCastingInMagicGetters(): void + { + $item = $this->productServiceScope->get($this->serviceId)->productService(); + $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations( + $item, + ProductServiceItemResult::class + ); + } +} diff --git a/tests/Integration/Services/Catalog/Product/ProductService/Service/ProductServiceTest.php b/tests/Integration/Services/Catalog/Product/ProductService/Service/ProductServiceTest.php new file mode 100644 index 00000000..7358901f --- /dev/null +++ b/tests/Integration/Services/Catalog/Product/ProductService/Service/ProductServiceTest.php @@ -0,0 +1,72 @@ + + * + * 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\Product\ProductService\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Catalog\Catalog\Service\Catalog; +use Bitrix24\SDK\Services\Catalog\Product\ProductService\Service\ProductService; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(ProductService::class)] +class ProductServiceTest extends TestCase +{ + private ProductService $productServiceScope; + + private Catalog $catalogService; + + #[\Override] + protected function setUp(): void + { + $this->productServiceScope = Factory::getServiceBuilder()->getCatalogScope()->productService(); + $this->catalogService = Factory::getServiceBuilder()->getCatalogScope()->catalog(); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[TestDox('test ProductService::add, get, update, delete, list, fieldsByFilter')] + public function testAddGetUpdateDeleteListFieldsByFilter(): void + { + $iblockId = $this->catalogService->list([], [], [], 1)->getCatalogs()[0]->iblockId; + + $addResult = $this->productServiceScope->add([ + 'iblockId' => $iblockId, + 'name' => sprintf('test service %s', time()), + ]); + $serviceId = $addResult->productService()->id; + $this->assertGreaterThan(0, $serviceId); + + $getResult = $this->productServiceScope->get($serviceId); + $this->assertEquals($serviceId, $getResult->productService()->id); + + $updated = $this->productServiceScope->update($serviceId, ['name' => 'updated service name']); + $this->assertEquals('updated service name', $updated->productService()->name); + + $listResult = $this->productServiceScope->list( + ['id', 'iblockId'], + ['id' => $serviceId, 'iblockId' => $iblockId] + ); + $this->assertCount(1, $listResult->getProductServices()); + + $fields = $this->productServiceScope->fieldsByFilter($iblockId); + $this->assertIsArray($fields->getFieldsDescription()); + + $this->assertTrue($this->productServiceScope->delete($serviceId)->isSuccess()); + } +} diff --git a/tests/Integration/Services/Catalog/Product/Service/ProductTest.php b/tests/Integration/Services/Catalog/Product/Service/ProductTest.php index 3e514c0f..8aeca05c 100644 --- a/tests/Integration/Services/Catalog/Product/Service/ProductTest.php +++ b/tests/Integration/Services/Catalog/Product/Service/ProductTest.php @@ -18,7 +18,7 @@ use Bitrix24\SDK\Services\Catalog\Catalog\Service\Catalog; use Bitrix24\SDK\Services\Catalog\Common\ProductType; use Bitrix24\SDK\Services\Catalog\Product\Service\Product; -use Bitrix24\SDK\Tests\Integration\Fabric; +use Bitrix24\SDK\Tests\Integration\Factory; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; use PHPUnit\Framework\Attributes\TestDox; @@ -150,9 +150,10 @@ public function testList():void $this->assertCount(1, $productsResult->getProducts()); } + #[\Override] protected function setUp(): void { - $this->productService = Fabric::getServiceBuilder()->getCatalogScope()->product(); - $this->catalogService = Fabric::getServiceBuilder()->getCatalogScope()->catalog(); + $this->productService = Factory::getServiceBuilder()->getCatalogScope()->product(); + $this->catalogService = Factory::getServiceBuilder()->getCatalogScope()->catalog(); } } \ No newline at end of file diff --git a/tests/Integration/Services/Catalog/Product/Sku/Result/SkuItemResultTest.php b/tests/Integration/Services/Catalog/Product/Sku/Result/SkuItemResultTest.php new file mode 100644 index 00000000..9cf5da20 --- /dev/null +++ b/tests/Integration/Services/Catalog/Product/Sku/Result/SkuItemResultTest.php @@ -0,0 +1,113 @@ + + * + * 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\Product\Sku\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Catalog\Catalog\Service\Catalog; +use Bitrix24\SDK\Services\Catalog\Product\Sku\Result\SkuItemResult; +use Bitrix24\SDK\Services\Catalog\Product\Sku\Service\Sku; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(SkuItemResult::class)] +class SkuItemResultTest extends TestCase +{ + use CustomBitrix24Assertions; + + private const FULL_SELECT = [ + 'id', 'iblockId', 'name', 'active', 'available', 'bundle', 'canBuyZero', 'code', + 'createdBy', 'dateActiveFrom', 'dateActiveTo', 'dateCreate', 'detailPicture', 'detailText', + 'detailTextType', 'height', 'iblockSection', 'iblockSectionId', 'length', 'measure', + 'modifiedBy', 'previewPicture', 'previewText', 'previewTextType', 'purchasingCurrency', + 'purchasingPrice', 'quantity', 'sort', 'subscribe', 'timestampX', 'type', 'vatId', + 'vatIncluded', 'weight', 'width', 'xmlId', + ]; + + private Sku $skuService; + + private int $skuId; + + private int $iblockId; + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function setUp(): void + { + $serviceBuilder = Factory::getServiceBuilder(); + $this->skuService = $serviceBuilder->getCatalogScope()->productSku(); + $catalogService = $serviceBuilder->getCatalogScope()->catalog(); + $this->iblockId = $catalogService->list([], [], [], 1)->getCatalogs()[0]->iblockId; + + $this->skuId = $this->skuService->add([ + 'iblockId' => $this->iblockId, + 'name' => sprintf('test sku annotations %s', time()), + ])->sku()->id; + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function tearDown(): void + { + $this->skuService->delete($this->skuId); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[Test] + #[TestDox('all fields in SkuItemResult are annotated in phpdoc and match with raw api response')] + public function testAllFieldsAreAnnotated(): void + { + $rawItem = $this->skuService->list( + self::FULL_SELECT, + ['id' => $this->skuId, 'iblockId' => $this->iblockId] + )->getCoreResponse()->getResponseData()->getResult()['units'][0]; + + $this->assertBitrix24AllResultItemFieldsAnnotated( + array_keys($rawItem), + SkuItemResult::class + ); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[Test] + #[TestDox('all fields in SkuItemResult have valid type casting in magic getters')] + public function testAllFieldsHasValidTypeCastingInMagicGetters(): void + { + $item = $this->skuService->list( + self::FULL_SELECT, + ['id' => $this->skuId, 'iblockId' => $this->iblockId] + )->getSkus()[0]; + + $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations( + $item, + SkuItemResult::class + ); + } +} diff --git a/tests/Integration/Services/Catalog/Product/Sku/Service/SkuTest.php b/tests/Integration/Services/Catalog/Product/Sku/Service/SkuTest.php new file mode 100644 index 00000000..740296f4 --- /dev/null +++ b/tests/Integration/Services/Catalog/Product/Sku/Service/SkuTest.php @@ -0,0 +1,72 @@ + + * + * 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\Product\Sku\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Catalog\Catalog\Service\Catalog; +use Bitrix24\SDK\Services\Catalog\Product\Sku\Service\Sku; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Sku::class)] +class SkuTest extends TestCase +{ + private Sku $skuService; + + private Catalog $catalogService; + + #[\Override] + protected function setUp(): void + { + $this->skuService = Factory::getServiceBuilder()->getCatalogScope()->productSku(); + $this->catalogService = Factory::getServiceBuilder()->getCatalogScope()->catalog(); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[TestDox('test Sku::add, get, update, delete, list, fieldsByFilter')] + public function testAddGetUpdateDeleteListFieldsByFilter(): void + { + $iblockId = $this->catalogService->list([], [], [], 1)->getCatalogs()[0]->iblockId; + + $addResult = $this->skuService->add([ + 'iblockId' => $iblockId, + 'name' => sprintf('test sku %s', time()), + ]); + $skuId = $addResult->sku()->id; + $this->assertGreaterThan(0, $skuId); + + $getResult = $this->skuService->get($skuId); + $this->assertEquals($skuId, $getResult->sku()->id); + + $updated = $this->skuService->update($skuId, ['name' => 'updated sku name']); + $this->assertEquals('updated sku name', $updated->sku()->name); + + $listResult = $this->skuService->list( + ['id', 'iblockId'], + ['id' => $skuId, 'iblockId' => $iblockId] + ); + $this->assertCount(1, $listResult->getSkus()); + + $fields = $this->skuService->fieldsByFilter($iblockId); + $this->assertIsArray($fields->getFieldsDescription()); + + $this->assertTrue($this->skuService->delete($skuId)->isSuccess()); + } +} diff --git a/tests/Unit/Services/Catalog/Product/Offer/Service/OfferTest.php b/tests/Unit/Services/Catalog/Product/Offer/Service/OfferTest.php new file mode 100644 index 00000000..777bdd52 --- /dev/null +++ b/tests/Unit/Services/Catalog/Product/Offer/Service/OfferTest.php @@ -0,0 +1,101 @@ + + * + * 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\Product\Offer\Service; + +use Bitrix24\SDK\Core\Response\Response; +use Bitrix24\SDK\Core\Result\DeletedItemResult; +use Bitrix24\SDK\Core\Result\FieldsResult; +use Bitrix24\SDK\Services\Catalog\Product\Offer\Result\OfferResult; +use Bitrix24\SDK\Services\Catalog\Product\Offer\Result\OffersResult; +use Bitrix24\SDK\Services\Catalog\Product\Offer\Service\Offer; +use Bitrix24\SDK\Tests\Unit\Stubs\NullCore; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\TestCase; +use Psr\Log\NullLogger; + +#[CoversClass(Offer::class)] +class OfferTest extends TestCase +{ + private Offer $service; + + #[\Override] + protected function setUp(): void + { + $this->service = new Offer(new NullCore(), new NullLogger()); + } + + #[Test] + public function testAddReturnsOfferResult(): void + { + $this->assertInstanceOf( + OfferResult::class, + $this->service->add(['iblockId' => 1, 'name' => 'test', 'parentId' => 1]) + ); + } + + #[Test] + public function testUpdateReturnsOfferResult(): void + { + $this->assertInstanceOf( + OfferResult::class, + $this->service->update(1, ['name' => 'test']) + ); + } + + #[Test] + public function testGetReturnsOfferResult(): void + { + $this->assertInstanceOf( + OfferResult::class, + $this->service->get(1) + ); + } + + #[Test] + public function testListReturnsOffersResult(): void + { + $this->assertInstanceOf( + OffersResult::class, + $this->service->list(['id', 'iblockId'], ['iblockId' => 1]) + ); + } + + #[Test] + public function testDeleteReturnsDeletedItemResult(): void + { + $this->assertInstanceOf( + DeletedItemResult::class, + $this->service->delete(1) + ); + } + + #[Test] + public function testFieldsByFilterReturnsFieldsResult(): void + { + $this->assertInstanceOf( + FieldsResult::class, + $this->service->fieldsByFilter(1) + ); + } + + #[Test] + public function testDownloadReturnsResponse(): void + { + $this->assertInstanceOf( + Response::class, + $this->service->download(1, 1, 'detailPicture') + ); + } +} diff --git a/tests/Unit/Services/Catalog/Product/ProductService/Service/ProductServiceTest.php b/tests/Unit/Services/Catalog/Product/ProductService/Service/ProductServiceTest.php new file mode 100644 index 00000000..cab6b9fd --- /dev/null +++ b/tests/Unit/Services/Catalog/Product/ProductService/Service/ProductServiceTest.php @@ -0,0 +1,101 @@ + + * + * 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\Product\ProductService\Service; + +use Bitrix24\SDK\Core\Response\Response; +use Bitrix24\SDK\Core\Result\DeletedItemResult; +use Bitrix24\SDK\Core\Result\FieldsResult; +use Bitrix24\SDK\Services\Catalog\Product\ProductService\Result\ProductServiceResult; +use Bitrix24\SDK\Services\Catalog\Product\ProductService\Result\ProductServicesResult; +use Bitrix24\SDK\Services\Catalog\Product\ProductService\Service\ProductService; +use Bitrix24\SDK\Tests\Unit\Stubs\NullCore; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\TestCase; +use Psr\Log\NullLogger; + +#[CoversClass(ProductService::class)] +class ProductServiceTest extends TestCase +{ + private ProductService $service; + + #[\Override] + protected function setUp(): void + { + $this->service = new ProductService(new NullCore(), new NullLogger()); + } + + #[Test] + public function testAddReturnsProductServiceResult(): void + { + $this->assertInstanceOf( + ProductServiceResult::class, + $this->service->add(['iblockId' => 1, 'name' => 'test']) + ); + } + + #[Test] + public function testUpdateReturnsProductServiceResult(): void + { + $this->assertInstanceOf( + ProductServiceResult::class, + $this->service->update(1, ['name' => 'test']) + ); + } + + #[Test] + public function testGetReturnsProductServiceResult(): void + { + $this->assertInstanceOf( + ProductServiceResult::class, + $this->service->get(1) + ); + } + + #[Test] + public function testListReturnsProductServicesResult(): void + { + $this->assertInstanceOf( + ProductServicesResult::class, + $this->service->list(['id', 'iblockId'], ['iblockId' => 1]) + ); + } + + #[Test] + public function testDeleteReturnsDeletedItemResult(): void + { + $this->assertInstanceOf( + DeletedItemResult::class, + $this->service->delete(1) + ); + } + + #[Test] + public function testFieldsByFilterReturnsFieldsResult(): void + { + $this->assertInstanceOf( + FieldsResult::class, + $this->service->fieldsByFilter(1) + ); + } + + #[Test] + public function testDownloadReturnsResponse(): void + { + $this->assertInstanceOf( + Response::class, + $this->service->download(1, 1, 'detailPicture') + ); + } +} diff --git a/tests/Unit/Services/Catalog/Product/Service/ProductTest.php b/tests/Unit/Services/Catalog/Product/Service/ProductTest.php new file mode 100644 index 00000000..a4e51378 --- /dev/null +++ b/tests/Unit/Services/Catalog/Product/Service/ProductTest.php @@ -0,0 +1,55 @@ + + * + * 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\Product\Service; + +use Bitrix24\SDK\Core\Response\Response; +use Bitrix24\SDK\Services\Catalog\Product\Result\ProductResult; +use Bitrix24\SDK\Services\Catalog\Product\Service\Batch; +use Bitrix24\SDK\Services\Catalog\Product\Service\Product; +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\TestCase; +use Psr\Log\NullLogger; + +#[CoversClass(Product::class)] +class ProductTest extends TestCase +{ + private Product $service; + + #[\Override] + protected function setUp(): void + { + $this->service = new Product(new Batch(new NullBatch(), new NullLogger()), new NullCore(), new NullLogger()); + } + + #[Test] + public function testUpdateReturnsProductResult(): void + { + $this->assertInstanceOf( + ProductResult::class, + $this->service->update(1, ['name' => 'test']) + ); + } + + #[Test] + public function testDownloadReturnsResponse(): void + { + $this->assertInstanceOf( + Response::class, + $this->service->download(1, 1, 'detailPicture') + ); + } +} diff --git a/tests/Unit/Services/Catalog/Product/Sku/Service/SkuTest.php b/tests/Unit/Services/Catalog/Product/Sku/Service/SkuTest.php new file mode 100644 index 00000000..ff635e9c --- /dev/null +++ b/tests/Unit/Services/Catalog/Product/Sku/Service/SkuTest.php @@ -0,0 +1,101 @@ + + * + * 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\Product\Sku\Service; + +use Bitrix24\SDK\Core\Response\Response; +use Bitrix24\SDK\Core\Result\DeletedItemResult; +use Bitrix24\SDK\Core\Result\FieldsResult; +use Bitrix24\SDK\Services\Catalog\Product\Sku\Result\SkuResult; +use Bitrix24\SDK\Services\Catalog\Product\Sku\Result\SkusResult; +use Bitrix24\SDK\Services\Catalog\Product\Sku\Service\Sku; +use Bitrix24\SDK\Tests\Unit\Stubs\NullCore; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\TestCase; +use Psr\Log\NullLogger; + +#[CoversClass(Sku::class)] +class SkuTest extends TestCase +{ + private Sku $service; + + #[\Override] + protected function setUp(): void + { + $this->service = new Sku(new NullCore(), new NullLogger()); + } + + #[Test] + public function testAddReturnsSkuResult(): void + { + $this->assertInstanceOf( + SkuResult::class, + $this->service->add(['iblockId' => 1, 'name' => 'test']) + ); + } + + #[Test] + public function testUpdateReturnsSkuResult(): void + { + $this->assertInstanceOf( + SkuResult::class, + $this->service->update(1, ['name' => 'test']) + ); + } + + #[Test] + public function testGetReturnsSkuResult(): void + { + $this->assertInstanceOf( + SkuResult::class, + $this->service->get(1) + ); + } + + #[Test] + public function testListReturnsSkusResult(): void + { + $this->assertInstanceOf( + SkusResult::class, + $this->service->list(['id', 'iblockId'], ['iblockId' => 1]) + ); + } + + #[Test] + public function testDeleteReturnsDeletedItemResult(): void + { + $this->assertInstanceOf( + DeletedItemResult::class, + $this->service->delete(1) + ); + } + + #[Test] + public function testFieldsByFilterReturnsFieldsResult(): void + { + $this->assertInstanceOf( + FieldsResult::class, + $this->service->fieldsByFilter(1) + ); + } + + #[Test] + public function testDownloadReturnsResponse(): void + { + $this->assertInstanceOf( + Response::class, + $this->service->download(1, 1, 'detailPicture') + ); + } +} From 53fa91bedab950bf1176dcb56eef8a917763b759 Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Thu, 9 Jul 2026 20:24:44 +0400 Subject: [PATCH 2/3] Fix on liter results --- .../Catalog/Catalog/Service/CatalogTest.php | 6 ++--- .../Offer/Result/OfferItemResultTest.php | 24 +++++++++++-------- .../Product/Offer/Service/OfferTest.php | 20 ++++++++-------- .../Result/ProductServiceItemResultTest.php | 18 +++++++------- .../Service/ProductServiceTest.php | 18 +++++++------- .../Catalog/Product/Service/ProductTest.php | 10 ++++---- .../Product/Sku/Result/SkuItemResultTest.php | 14 ++++------- .../Catalog/Product/Sku/Service/SkuTest.php | 18 +++++++------- 8 files changed, 65 insertions(+), 63 deletions(-) diff --git a/tests/Integration/Services/Catalog/Catalog/Service/CatalogTest.php b/tests/Integration/Services/Catalog/Catalog/Service/CatalogTest.php index cedf54fa..f98f2987 100644 --- a/tests/Integration/Services/Catalog/Catalog/Service/CatalogTest.php +++ b/tests/Integration/Services/Catalog/Catalog/Service/CatalogTest.php @@ -16,7 +16,7 @@ use Bitrix24\SDK\Core\Exceptions\BaseException; use Bitrix24\SDK\Core\Exceptions\TransportException; use Bitrix24\SDK\Services\Catalog\Catalog\Service\Catalog; -use Bitrix24\SDK\Tests\Integration\Factory; +use Bitrix24\SDK\Tests\Integration\Fabric; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\TestCase; @@ -60,6 +60,6 @@ public function testGet(): void #[\Override] protected function setUp(): void { - $this->service = Factory::getServiceBuilder()->getCatalogScope()->catalog(); + $this->service = Fabric::getServiceBuilder()->getCatalogScope()->catalog(); } -} \ No newline at end of file +} diff --git a/tests/Integration/Services/Catalog/Product/Offer/Result/OfferItemResultTest.php b/tests/Integration/Services/Catalog/Product/Offer/Result/OfferItemResultTest.php index ab874da7..04121a49 100644 --- a/tests/Integration/Services/Catalog/Product/Offer/Result/OfferItemResultTest.php +++ b/tests/Integration/Services/Catalog/Product/Offer/Result/OfferItemResultTest.php @@ -20,7 +20,7 @@ use Bitrix24\SDK\Services\Catalog\Product\Offer\Service\Offer; use Bitrix24\SDK\Services\Catalog\Product\Sku\Service\Sku; use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; -use Bitrix24\SDK\Tests\Integration\Factory; +use Bitrix24\SDK\Tests\Integration\Fabric; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\TestDox; @@ -39,6 +39,8 @@ class OfferItemResultTest extends TestCase private int $offerId; + private int $offersCatalogIblockId; + /** * @throws BaseException * @throws TransportException @@ -46,21 +48,23 @@ class OfferItemResultTest extends TestCase #[\Override] protected function setUp(): void { - $serviceBuilder = Factory::getServiceBuilder(); + $serviceBuilder = Fabric::getServiceBuilder(); $this->offerService = $serviceBuilder->getCatalogScope()->productOffer(); $this->skuService = $serviceBuilder->getCatalogScope()->productSku(); $catalogService = $serviceBuilder->getCatalogScope()->catalog(); $productCatalog = null; $offersCatalog = null; - foreach ($catalogService->list([], [], [], 1)->getCatalogs() as $catalog) { - if ($catalog->productIblockId === null) { - $productCatalog = $catalog; + foreach ($catalogService->list([], [], [], 1)->getCatalogs() as $catalogItemResult) { + if ($catalogItemResult->productIblockId === null) { + $productCatalog = $catalogItemResult; } else { - $offersCatalog = $catalog; + $offersCatalog = $catalogItemResult; } } + $this->offersCatalogIblockId = $offersCatalog->iblockId; + $this->skuId = $this->skuService->add([ 'iblockId' => $productCatalog->iblockId, 'name' => sprintf('test sku for offer annotations %s', time()), @@ -98,7 +102,7 @@ public function testAllFieldsAreAnnotated(): void // dynamic catalog properties (propertyN) vary per portal and are intentionally not annotated $fieldCodes = array_filter( array_keys($rawItem), - static fn (string $fieldCode): bool => !preg_match('/^property\d+$/', $fieldCode) + static fn (string $fieldCode): bool => in_array(preg_match('/^property\d+$/', $fieldCode), [0, false], true) ); $this->assertBitrix24AllResultItemFieldsAnnotated( @@ -115,9 +119,9 @@ public function testAllFieldsAreAnnotated(): void #[TestDox('all fields in OfferItemResult have valid type casting in magic getters')] public function testAllFieldsHasValidTypeCastingInMagicGetters(): void { - $item = $this->offerService->get($this->offerId)->offer(); - $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations( - $item, + $fields = $this->offerService->fieldsByFilter($this->offersCatalogIblockId)->getFieldsDescription(); + $this->assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( + $fields, OfferItemResult::class ); } diff --git a/tests/Integration/Services/Catalog/Product/Offer/Service/OfferTest.php b/tests/Integration/Services/Catalog/Product/Offer/Service/OfferTest.php index 91288761..a99e5c51 100644 --- a/tests/Integration/Services/Catalog/Product/Offer/Service/OfferTest.php +++ b/tests/Integration/Services/Catalog/Product/Offer/Service/OfferTest.php @@ -18,7 +18,7 @@ use Bitrix24\SDK\Services\Catalog\Catalog\Service\Catalog; use Bitrix24\SDK\Services\Catalog\Product\Offer\Service\Offer; use Bitrix24\SDK\Services\Catalog\Product\Sku\Service\Sku; -use Bitrix24\SDK\Tests\Integration\Factory; +use Bitrix24\SDK\Tests\Integration\Fabric; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\TestCase; @@ -35,9 +35,9 @@ class OfferTest extends TestCase #[\Override] protected function setUp(): void { - $this->offerService = Factory::getServiceBuilder()->getCatalogScope()->productOffer(); - $this->skuService = Factory::getServiceBuilder()->getCatalogScope()->productSku(); - $this->catalogService = Factory::getServiceBuilder()->getCatalogScope()->catalog(); + $this->offerService = Fabric::getServiceBuilder()->getCatalogScope()->productOffer(); + $this->skuService = Fabric::getServiceBuilder()->getCatalogScope()->productSku(); + $this->catalogService = Fabric::getServiceBuilder()->getCatalogScope()->catalog(); } /** @@ -66,12 +66,12 @@ public function testAddGetUpdateDeleteListFieldsByFilter(): void 'name' => sprintf('test sku for offer %s', time()), ])->sku()->id; - $addResult = $this->offerService->add([ + $offerResult = $this->offerService->add([ 'iblockId' => $offersCatalog->iblockId, 'name' => sprintf('test offer %s', time()), 'parentId' => $skuId, ]); - $offerId = $addResult->offer()->id; + $offerId = $offerResult->offer()->id; $this->assertGreaterThan(0, $offerId); $getResult = $this->offerService->get($offerId); @@ -80,14 +80,14 @@ public function testAddGetUpdateDeleteListFieldsByFilter(): void $updated = $this->offerService->update($offerId, ['name' => 'updated offer name']); $this->assertEquals('updated offer name', $updated->offer()->name); - $listResult = $this->offerService->list( + $offersResult = $this->offerService->list( ['id', 'iblockId'], ['id' => $offerId, 'iblockId' => $offersCatalog->iblockId] ); - $this->assertCount(1, $listResult->getOffers()); + $this->assertCount(1, $offersResult->getOffers()); - $fields = $this->offerService->fieldsByFilter($offersCatalog->iblockId); - $this->assertIsArray($fields->getFieldsDescription()); + $fieldsResult = $this->offerService->fieldsByFilter($offersCatalog->iblockId); + $this->assertIsArray($fieldsResult->getFieldsDescription()); $this->assertTrue($this->offerService->delete($offerId)->isSuccess()); $this->assertTrue($this->skuService->delete($skuId)->isSuccess()); diff --git a/tests/Integration/Services/Catalog/Product/ProductService/Result/ProductServiceItemResultTest.php b/tests/Integration/Services/Catalog/Product/ProductService/Result/ProductServiceItemResultTest.php index fcc4f3c3..24a17e42 100644 --- a/tests/Integration/Services/Catalog/Product/ProductService/Result/ProductServiceItemResultTest.php +++ b/tests/Integration/Services/Catalog/Product/ProductService/Result/ProductServiceItemResultTest.php @@ -19,7 +19,7 @@ use Bitrix24\SDK\Services\Catalog\Product\ProductService\Result\ProductServiceItemResult; use Bitrix24\SDK\Services\Catalog\Product\ProductService\Service\ProductService; use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; -use Bitrix24\SDK\Tests\Integration\Factory; +use Bitrix24\SDK\Tests\Integration\Fabric; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\TestDox; @@ -34,6 +34,8 @@ class ProductServiceItemResultTest extends TestCase private int $serviceId; + private int $iblockId; + /** * @throws BaseException * @throws TransportException @@ -41,13 +43,13 @@ class ProductServiceItemResultTest extends TestCase #[\Override] protected function setUp(): void { - $serviceBuilder = Factory::getServiceBuilder(); + $serviceBuilder = Fabric::getServiceBuilder(); $this->productServiceScope = $serviceBuilder->getCatalogScope()->productService(); $catalogService = $serviceBuilder->getCatalogScope()->catalog(); - $iblockId = $catalogService->list([], [], [], 1)->getCatalogs()[0]->iblockId; + $this->iblockId = $catalogService->list([], [], [], 1)->getCatalogs()[0]->iblockId; $this->serviceId = $this->productServiceScope->add([ - 'iblockId' => $iblockId, + 'iblockId' => $this->iblockId, 'name' => sprintf('test service annotations %s', time()), ])->productService()->id; } @@ -76,7 +78,7 @@ public function testAllFieldsAreAnnotated(): void // dynamic catalog properties (propertyN) vary per portal and are intentionally not annotated $fieldCodes = array_filter( array_keys($rawItem), - static fn (string $fieldCode): bool => !preg_match('/^property\d+$/', $fieldCode) + static fn (string $fieldCode): bool => in_array(preg_match('/^property\d+$/', $fieldCode), [0, false], true) ); $this->assertBitrix24AllResultItemFieldsAnnotated( @@ -93,9 +95,9 @@ public function testAllFieldsAreAnnotated(): void #[TestDox('all fields in ProductServiceItemResult have valid type casting in magic getters')] public function testAllFieldsHasValidTypeCastingInMagicGetters(): void { - $item = $this->productServiceScope->get($this->serviceId)->productService(); - $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations( - $item, + $fields = $this->productServiceScope->fieldsByFilter($this->iblockId)->getFieldsDescription(); + $this->assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( + $fields, ProductServiceItemResult::class ); } diff --git a/tests/Integration/Services/Catalog/Product/ProductService/Service/ProductServiceTest.php b/tests/Integration/Services/Catalog/Product/ProductService/Service/ProductServiceTest.php index 7358901f..a4384975 100644 --- a/tests/Integration/Services/Catalog/Product/ProductService/Service/ProductServiceTest.php +++ b/tests/Integration/Services/Catalog/Product/ProductService/Service/ProductServiceTest.php @@ -17,7 +17,7 @@ use Bitrix24\SDK\Core\Exceptions\TransportException; use Bitrix24\SDK\Services\Catalog\Catalog\Service\Catalog; use Bitrix24\SDK\Services\Catalog\Product\ProductService\Service\ProductService; -use Bitrix24\SDK\Tests\Integration\Factory; +use Bitrix24\SDK\Tests\Integration\Fabric; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\TestCase; @@ -32,8 +32,8 @@ class ProductServiceTest extends TestCase #[\Override] protected function setUp(): void { - $this->productServiceScope = Factory::getServiceBuilder()->getCatalogScope()->productService(); - $this->catalogService = Factory::getServiceBuilder()->getCatalogScope()->catalog(); + $this->productServiceScope = Fabric::getServiceBuilder()->getCatalogScope()->productService(); + $this->catalogService = Fabric::getServiceBuilder()->getCatalogScope()->catalog(); } /** @@ -45,11 +45,11 @@ public function testAddGetUpdateDeleteListFieldsByFilter(): void { $iblockId = $this->catalogService->list([], [], [], 1)->getCatalogs()[0]->iblockId; - $addResult = $this->productServiceScope->add([ + $productServiceResult = $this->productServiceScope->add([ 'iblockId' => $iblockId, 'name' => sprintf('test service %s', time()), ]); - $serviceId = $addResult->productService()->id; + $serviceId = $productServiceResult->productService()->id; $this->assertGreaterThan(0, $serviceId); $getResult = $this->productServiceScope->get($serviceId); @@ -58,14 +58,14 @@ public function testAddGetUpdateDeleteListFieldsByFilter(): void $updated = $this->productServiceScope->update($serviceId, ['name' => 'updated service name']); $this->assertEquals('updated service name', $updated->productService()->name); - $listResult = $this->productServiceScope->list( + $productServicesResult = $this->productServiceScope->list( ['id', 'iblockId'], ['id' => $serviceId, 'iblockId' => $iblockId] ); - $this->assertCount(1, $listResult->getProductServices()); + $this->assertCount(1, $productServicesResult->getProductServices()); - $fields = $this->productServiceScope->fieldsByFilter($iblockId); - $this->assertIsArray($fields->getFieldsDescription()); + $fieldsResult = $this->productServiceScope->fieldsByFilter($iblockId); + $this->assertIsArray($fieldsResult->getFieldsDescription()); $this->assertTrue($this->productServiceScope->delete($serviceId)->isSuccess()); } diff --git a/tests/Integration/Services/Catalog/Product/Service/ProductTest.php b/tests/Integration/Services/Catalog/Product/Service/ProductTest.php index 8aeca05c..817baa61 100644 --- a/tests/Integration/Services/Catalog/Product/Service/ProductTest.php +++ b/tests/Integration/Services/Catalog/Product/Service/ProductTest.php @@ -18,7 +18,7 @@ use Bitrix24\SDK\Services\Catalog\Catalog\Service\Catalog; use Bitrix24\SDK\Services\Catalog\Common\ProductType; use Bitrix24\SDK\Services\Catalog\Product\Service\Product; -use Bitrix24\SDK\Tests\Integration\Factory; +use Bitrix24\SDK\Tests\Integration\Fabric; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; use PHPUnit\Framework\Attributes\TestDox; @@ -123,7 +123,7 @@ public function testDelete(): void * @throws TransportException If there is a transport exception thrown during the process of listing products. */ #[TestDox('test Product::list')] - public function testList():void + public function testList(): void { $iblockId = $this->catalogService->list([], [], [], 1)->getCatalogs()[0]->iblockId; $fields = [ @@ -153,7 +153,7 @@ public function testList():void #[\Override] protected function setUp(): void { - $this->productService = Factory::getServiceBuilder()->getCatalogScope()->product(); - $this->catalogService = Factory::getServiceBuilder()->getCatalogScope()->catalog(); + $this->productService = Fabric::getServiceBuilder()->getCatalogScope()->product(); + $this->catalogService = Fabric::getServiceBuilder()->getCatalogScope()->catalog(); } -} \ No newline at end of file +} diff --git a/tests/Integration/Services/Catalog/Product/Sku/Result/SkuItemResultTest.php b/tests/Integration/Services/Catalog/Product/Sku/Result/SkuItemResultTest.php index 9cf5da20..ce6b4df0 100644 --- a/tests/Integration/Services/Catalog/Product/Sku/Result/SkuItemResultTest.php +++ b/tests/Integration/Services/Catalog/Product/Sku/Result/SkuItemResultTest.php @@ -19,7 +19,7 @@ use Bitrix24\SDK\Services\Catalog\Product\Sku\Result\SkuItemResult; use Bitrix24\SDK\Services\Catalog\Product\Sku\Service\Sku; use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; -use Bitrix24\SDK\Tests\Integration\Factory; +use Bitrix24\SDK\Tests\Integration\Fabric; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\TestDox; @@ -52,7 +52,7 @@ class SkuItemResultTest extends TestCase #[\Override] protected function setUp(): void { - $serviceBuilder = Factory::getServiceBuilder(); + $serviceBuilder = Fabric::getServiceBuilder(); $this->skuService = $serviceBuilder->getCatalogScope()->productSku(); $catalogService = $serviceBuilder->getCatalogScope()->catalog(); $this->iblockId = $catalogService->list([], [], [], 1)->getCatalogs()[0]->iblockId; @@ -100,13 +100,9 @@ public function testAllFieldsAreAnnotated(): void #[TestDox('all fields in SkuItemResult have valid type casting in magic getters')] public function testAllFieldsHasValidTypeCastingInMagicGetters(): void { - $item = $this->skuService->list( - self::FULL_SELECT, - ['id' => $this->skuId, 'iblockId' => $this->iblockId] - )->getSkus()[0]; - - $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations( - $item, + $fields = $this->skuService->fieldsByFilter($this->iblockId)->getFieldsDescription(); + $this->assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( + $fields, SkuItemResult::class ); } diff --git a/tests/Integration/Services/Catalog/Product/Sku/Service/SkuTest.php b/tests/Integration/Services/Catalog/Product/Sku/Service/SkuTest.php index 740296f4..7f23622e 100644 --- a/tests/Integration/Services/Catalog/Product/Sku/Service/SkuTest.php +++ b/tests/Integration/Services/Catalog/Product/Sku/Service/SkuTest.php @@ -17,7 +17,7 @@ use Bitrix24\SDK\Core\Exceptions\TransportException; use Bitrix24\SDK\Services\Catalog\Catalog\Service\Catalog; use Bitrix24\SDK\Services\Catalog\Product\Sku\Service\Sku; -use Bitrix24\SDK\Tests\Integration\Factory; +use Bitrix24\SDK\Tests\Integration\Fabric; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\TestCase; @@ -32,8 +32,8 @@ class SkuTest extends TestCase #[\Override] protected function setUp(): void { - $this->skuService = Factory::getServiceBuilder()->getCatalogScope()->productSku(); - $this->catalogService = Factory::getServiceBuilder()->getCatalogScope()->catalog(); + $this->skuService = Fabric::getServiceBuilder()->getCatalogScope()->productSku(); + $this->catalogService = Fabric::getServiceBuilder()->getCatalogScope()->catalog(); } /** @@ -45,11 +45,11 @@ public function testAddGetUpdateDeleteListFieldsByFilter(): void { $iblockId = $this->catalogService->list([], [], [], 1)->getCatalogs()[0]->iblockId; - $addResult = $this->skuService->add([ + $skuResult = $this->skuService->add([ 'iblockId' => $iblockId, 'name' => sprintf('test sku %s', time()), ]); - $skuId = $addResult->sku()->id; + $skuId = $skuResult->sku()->id; $this->assertGreaterThan(0, $skuId); $getResult = $this->skuService->get($skuId); @@ -58,14 +58,14 @@ public function testAddGetUpdateDeleteListFieldsByFilter(): void $updated = $this->skuService->update($skuId, ['name' => 'updated sku name']); $this->assertEquals('updated sku name', $updated->sku()->name); - $listResult = $this->skuService->list( + $skusResult = $this->skuService->list( ['id', 'iblockId'], ['id' => $skuId, 'iblockId' => $iblockId] ); - $this->assertCount(1, $listResult->getSkus()); + $this->assertCount(1, $skusResult->getSkus()); - $fields = $this->skuService->fieldsByFilter($iblockId); - $this->assertIsArray($fields->getFieldsDescription()); + $fieldsResult = $this->skuService->fieldsByFilter($iblockId); + $this->assertIsArray($fieldsResult->getFieldsDescription()); $this->assertTrue($this->skuService->delete($skuId)->isSuccess()); } From b713db2bf3abb6aea7794cbca0b2b7b641c837d7 Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Thu, 9 Jul 2026 21:24:52 +0400 Subject: [PATCH 3/3] Fix on test results --- Makefile | 12 ++-- .../Common/Result/AbstractCatalogItem.php | 6 +- .../Product/Offer/Result/OfferItemResult.php | 20 +++---- .../Product/Result/ProductItemResult.php | 4 +- .../Product/Sku/Result/SkuItemResult.php | 12 ++-- .../CustomBitrix24Assertions.php | 56 +++++++++++++++---- .../Offer/Result/OfferItemResultTest.php | 9 ++- .../Result/ProductServiceItemResultTest.php | 8 ++- .../Product/Sku/Result/SkuItemResultTest.php | 8 ++- 9 files changed, 95 insertions(+), 40 deletions(-) diff --git a/Makefile b/Makefile index d773c0a3..c3d2488a 100644 --- a/Makefile +++ b/Makefile @@ -500,27 +500,27 @@ test-integration-landing-repowidget: .PHONY: test-integration-scope-catalog test-integration-scope-catalog: - docker compose run --rm php-cli $(PHPUNIT) --testsuite integration_tests_scope_catalog + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_scope_catalog .PHONY: test-integration-catalog-catalog test-integration-catalog-catalog: - docker compose run --rm php-cli $(PHPUNIT) --testsuite integration_tests_catalog_catalog + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_catalog_catalog .PHONY: test-integration-catalog-product test-integration-catalog-product: - docker compose run --rm php-cli $(PHPUNIT) --testsuite integration_tests_catalog_product + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_catalog_product .PHONY: test-integration-catalog-product-service test-integration-catalog-product-service: - docker compose run --rm php-cli $(PHPUNIT) --testsuite integration_tests_catalog_product_service + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_catalog_product_service .PHONY: test-integration-catalog-product-sku test-integration-catalog-product-sku: - docker compose run --rm php-cli $(PHPUNIT) --testsuite integration_tests_catalog_product_sku + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_catalog_product_sku .PHONY: test-integration-catalog-product-offer test-integration-catalog-product-offer: - docker compose run --rm php-cli $(PHPUNIT) --testsuite integration_tests_catalog_product_offer + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_catalog_product_offer # work dev environment .PHONY: php-dev-server-up diff --git a/src/Services/Catalog/Common/Result/AbstractCatalogItem.php b/src/Services/Catalog/Common/Result/AbstractCatalogItem.php index 822df379..762af872 100644 --- a/src/Services/Catalog/Common/Result/AbstractCatalogItem.php +++ b/src/Services/Catalog/Common/Result/AbstractCatalogItem.php @@ -47,9 +47,13 @@ public function __get($offset) case 'available': case 'bundle': case 'vatIncluded': + case 'withoutOrder': + case 'subscribe': + case 'quantityTrace': return $this->data[$offset] === 'Y'; case 'barcodeMulti': case 'canBuyZero': + case 'recurSchemeType': if ($this->data[$offset] !== null) { return $this->data[$offset] === 'Y'; } @@ -69,8 +73,6 @@ public function __get($offset) case 'id': case 'modifiedBy': case 'sort': - case 'height': - case 'length': case 'vatId': if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { return (int)$this->data[$offset]; diff --git a/src/Services/Catalog/Product/Offer/Result/OfferItemResult.php b/src/Services/Catalog/Product/Offer/Result/OfferItemResult.php index 70d05421..7d58d2ab 100644 --- a/src/Services/Catalog/Product/Offer/Result/OfferItemResult.php +++ b/src/Services/Catalog/Product/Offer/Result/OfferItemResult.php @@ -31,12 +31,12 @@ * @property-read array|null $detailPicture * @property-read string $detailText * @property-read string $detailTextType - * @property-read ?int $height + * @property-read ?string $height * @property-read int $id * @property-read int $iblockId * @property-read ?array $iblockSection * @property-read ?int $iblockSectionId - * @property-read ?int $length + * @property-read ?string $length * @property-read ?int $measure * @property-read int $modifiedBy * @property-read string $name @@ -46,21 +46,21 @@ * @property-read string $previewTextType * @property-read ?string $purchasingCurrency * @property-read ?string $purchasingPrice - * @property-read ?int $quantity - * @property-read ?int $quantityReserved - * @property-read string $quantityTrace + * @property-read ?string $quantity + * @property-read ?string $quantityReserved + * @property-read bool $quantityTrace * @property-read ?int $recurSchemeLength - * @property-read ?string $recurSchemeType + * @property-read ?bool $recurSchemeType * @property-read int $sort - * @property-read string $subscribe + * @property-read bool $subscribe * @property-read CarbonImmutable $timestampX * @property-read ?int $trialPriceId * @property-read ProductType $type * @property-read ?int $vatId * @property-read bool $vatIncluded - * @property-read ?int $weight - * @property-read ?int $width - * @property-read string $withoutOrder + * @property-read ?string $weight + * @property-read ?string $width + * @property-read bool $withoutOrder * @property-read string $xmlId */ class OfferItemResult extends AbstractCatalogItem diff --git a/src/Services/Catalog/Product/Result/ProductItemResult.php b/src/Services/Catalog/Product/Result/ProductItemResult.php index 58c80851..d8e6fa29 100644 --- a/src/Services/Catalog/Product/Result/ProductItemResult.php +++ b/src/Services/Catalog/Product/Result/ProductItemResult.php @@ -37,8 +37,8 @@ * @property-read int $iblockId * @property-read int $iblockSectionId * @property-read int $modifiedBy - * @property-read ?int $height - * @property-read ?int $length + * @property-read ?string $height + * @property-read ?string $length * @property-read mixed $measure * @property-read string $name * @property-read array|null $previewPicture diff --git a/src/Services/Catalog/Product/Sku/Result/SkuItemResult.php b/src/Services/Catalog/Product/Sku/Result/SkuItemResult.php index 7df55bc5..337e5b8a 100644 --- a/src/Services/Catalog/Product/Sku/Result/SkuItemResult.php +++ b/src/Services/Catalog/Product/Sku/Result/SkuItemResult.php @@ -30,12 +30,12 @@ * @property-read array|null $detailPicture * @property-read string $detailText * @property-read string $detailTextType - * @property-read ?int $height + * @property-read ?string $height * @property-read int $id * @property-read int $iblockId * @property-read ?array $iblockSection * @property-read ?int $iblockSectionId - * @property-read ?int $length + * @property-read ?string $length * @property-read ?int $measure * @property-read int $modifiedBy * @property-read string $name @@ -44,15 +44,15 @@ * @property-read string $previewTextType * @property-read ?string $purchasingCurrency * @property-read ?string $purchasingPrice - * @property-read ?int $quantity + * @property-read ?string $quantity * @property-read int $sort - * @property-read string $subscribe + * @property-read bool $subscribe * @property-read CarbonImmutable $timestampX * @property-read ProductType $type * @property-read ?int $vatId * @property-read bool $vatIncluded - * @property-read ?int $weight - * @property-read ?int $width + * @property-read ?string $weight + * @property-read ?string $width * @property-read string $xmlId */ class SkuItemResult extends AbstractCatalogItem diff --git a/tests/CustomAssertions/CustomBitrix24Assertions.php b/tests/CustomAssertions/CustomBitrix24Assertions.php index dd1d32b7..4a47e163 100644 --- a/tests/CustomAssertions/CustomBitrix24Assertions.php +++ b/tests/CustomAssertions/CustomBitrix24Assertions.php @@ -19,18 +19,19 @@ use Bitrix24\SDK\Services\CRM\Activity\ActivityPriority; use Bitrix24\SDK\Services\CRM\Activity\ActivityStatus; use Bitrix24\SDK\Services\CRM\Activity\ActivityType; +use Bitrix24\SDK\Services\Catalog\Common\ProductType; use Carbon\CarbonImmutable; use MoneyPHP\Percentage\Percentage; use Typhoon\Reflection\TyphoonReflector; -use function Typhoon\Type\stringify; use Money\Currency; +use function Typhoon\Type\stringify; + trait CustomBitrix24Assertions { /** * @param array $fieldCodesFromApi * @param class-string $resultItemClassName - * @return void */ protected function assertBitrix24AllResultItemFieldsAnnotated( array $fieldCodesFromApi, @@ -39,16 +40,17 @@ protected function assertBitrix24AllResultItemFieldsAnnotated( sort($fieldCodesFromApi); // parse keys from phpdoc annotation - $props = TyphoonReflector::build()->reflectClass($resultItemClassName)->properties(); + $collection = TyphoonReflector::build()->reflectClass($resultItemClassName)->properties(); $propsFromAnnotations = []; - foreach ($props as $meta) { + foreach ($collection as $meta) { if ($meta->isAnnotated() && !$meta->isNative()) { $propsFromAnnotations[] = $meta->id->name; } } + sort($propsFromAnnotations); - if (count($fieldCodesFromApi) >= $propsFromAnnotations) { + if (count($fieldCodesFromApi) >= count($propsFromAnnotations)) { $this->assertEquals( $fieldCodesFromApi, $propsFromAnnotations, @@ -76,9 +78,9 @@ protected function assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( string $resultItemClassName ): void { // parse keys from phpdoc annotation - $props = TyphoonReflector::build()->reflectClass($resultItemClassName)->properties(); + $collection = TyphoonReflector::build()->reflectClass($resultItemClassName)->properties(); $propsFromAnnotations = []; - foreach ($props as $meta) { + foreach ($collection as $meta) { if ($meta->isAnnotated() && !$meta->isNative()) { $propsFromAnnotations[$meta->id->name] = stringify($meta->type()); } @@ -105,6 +107,7 @@ protected function assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( ); break; } + // if field code contains currency if (str_contains($fieldCode, 'CURRENCY_ID')) { $this->assertTrue( @@ -120,6 +123,7 @@ protected function assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( ); break; } + if (str_contains($fieldCode, 'EDIT_FORM_LABEL') || str_contains($fieldCode, 'LIST_COLUMN_LABEL') || str_contains($fieldCode, 'LIST_FILTER_LABEL') @@ -156,6 +160,21 @@ protected function assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( case 'integer': case 'int': case 'mail_message': + if ($fieldCode === 'type') { + $this->assertTrue( + str_contains($propsFromAnnotations[$fieldCode], ProductType::class), + sprintf( + 'class «%s» field «%s» has invalid type phpdoc annotation «%s», field type from bitrix24 is «%s», expected sdk-type «%s»', + $resultItemClassName, + $fieldCode, + $propsFromAnnotations[$fieldCode], + $fieldData['type'], + ProductType::class + ) + ); + break; + } + $this->assertTrue( str_contains($propsFromAnnotations[$fieldCode], 'int'), sprintf( @@ -183,6 +202,7 @@ protected function assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( ); break; } + if (str_contains(mb_strtoupper($fieldCode), 'QUANTITY')) { $this->assertTrue( str_contains($propsFromAnnotations[$fieldCode], 'string'), @@ -197,7 +217,12 @@ protected function assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( ); break; } - if (str_contains(mb_strtoupper($fieldCode), 'WEIGHT')) { + + if (str_contains(mb_strtoupper($fieldCode), 'WEIGHT') + || str_contains(mb_strtoupper($fieldCode), 'HEIGHT') + || str_contains(mb_strtoupper($fieldCode), 'LENGTH') + || str_contains(mb_strtoupper($fieldCode), 'WIDTH') + ) { $this->assertTrue( str_contains($propsFromAnnotations[$fieldCode], 'string'), sprintf( @@ -211,6 +236,7 @@ protected function assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( ); break; } + if (str_contains(mb_strtoupper($fieldCode), 'RATE')) { $this->assertTrue( str_contains($propsFromAnnotations[$fieldCode], Percentage::class), @@ -225,8 +251,9 @@ protected function assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( ); break; } + $this->assertTrue( - str_contains($propsFromAnnotations[$fieldCode], 'Money\Money'), + str_contains($propsFromAnnotations[$fieldCode], \Money\Money::class), sprintf( 'class «%s» field «%s» has invalid type phpdoc annotation «%s», field type from bitrix24 is «%s», expected sdk-type «%s»', $resultItemClassName, @@ -280,6 +307,7 @@ protected function assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( break; } + if (str_contains($fieldCode, 'durationType') || str_contains($fieldCode, 'mark') || str_contains($fieldCode, 'TYPE') @@ -298,6 +326,7 @@ protected function assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( break; } + if (str_contains($fieldCode, 'priority') || str_contains($fieldCode, 'status') ) { @@ -315,6 +344,7 @@ protected function assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( break; } + $this->assertTrue( str_contains($propsFromAnnotations[$fieldCode], 'bool'), sprintf( @@ -329,14 +359,15 @@ protected function assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( break; case 'file': $this->assertTrue( - str_contains($propsFromAnnotations[$fieldCode], 'File'), + str_contains($propsFromAnnotations[$fieldCode], 'File') + || str_contains($propsFromAnnotations[$fieldCode], 'array'), sprintf( 'class «%s» field «%s» has invalid type phpdoc annotation «%s», field type from bitrix24 is «%s», expected sdk-type «%s»', $resultItemClassName, $fieldCode, $propsFromAnnotations[$fieldCode], $fieldData['type'], - 'File|null' + 'File|null or array|null' ) ); break; @@ -365,6 +396,7 @@ protected function assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( ); break; } + $this->assertTrue( str_contains($propsFromAnnotations[$fieldCode], 'int'), sprintf( @@ -462,6 +494,8 @@ protected function assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( ); break; case 'array': + case 'list': + case 'productproperty': case 'crm': case 'crm_activity_binding': case 'crm_activity_communication': diff --git a/tests/Integration/Services/Catalog/Product/Offer/Result/OfferItemResultTest.php b/tests/Integration/Services/Catalog/Product/Offer/Result/OfferItemResultTest.php index 04121a49..a00b5b6a 100644 --- a/tests/Integration/Services/Catalog/Product/Offer/Result/OfferItemResultTest.php +++ b/tests/Integration/Services/Catalog/Product/Offer/Result/OfferItemResultTest.php @@ -119,7 +119,14 @@ public function testAllFieldsAreAnnotated(): void #[TestDox('all fields in OfferItemResult have valid type casting in magic getters')] public function testAllFieldsHasValidTypeCastingInMagicGetters(): void { - $fields = $this->offerService->fieldsByFilter($this->offersCatalogIblockId)->getFieldsDescription(); + // priceType and negativeAmountTrace are present only in fieldsByFilter response, not in item response, and are not annotated + // dynamic catalog properties (propertyN) vary per portal and are intentionally not annotated + $fieldsNotInItemResponse = ['priceType', 'negativeAmountTrace']; + $fields = array_filter( + $this->offerService->fieldsByFilter($this->offersCatalogIblockId)->getFieldsDescription()['offer'], + static fn (string $fieldCode): bool => !in_array($fieldCode, $fieldsNotInItemResponse, true) && in_array(preg_match('/^property\d+$/', $fieldCode), [0, false], true), + ARRAY_FILTER_USE_KEY + ); $this->assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( $fields, OfferItemResult::class diff --git a/tests/Integration/Services/Catalog/Product/ProductService/Result/ProductServiceItemResultTest.php b/tests/Integration/Services/Catalog/Product/ProductService/Result/ProductServiceItemResultTest.php index 24a17e42..81888685 100644 --- a/tests/Integration/Services/Catalog/Product/ProductService/Result/ProductServiceItemResultTest.php +++ b/tests/Integration/Services/Catalog/Product/ProductService/Result/ProductServiceItemResultTest.php @@ -95,7 +95,13 @@ public function testAllFieldsAreAnnotated(): void #[TestDox('all fields in ProductServiceItemResult have valid type casting in magic getters')] public function testAllFieldsHasValidTypeCastingInMagicGetters(): void { - $fields = $this->productServiceScope->fieldsByFilter($this->iblockId)->getFieldsDescription(); + // priceType is present only in fieldsByFilter response, not in item response, and is not annotated + // dynamic catalog properties (propertyN) vary per portal and are intentionally not annotated + $fields = array_filter( + $this->productServiceScope->fieldsByFilter($this->iblockId)->getFieldsDescription()['service'], + static fn (string $fieldCode): bool => $fieldCode !== 'priceType' && in_array(preg_match('/^property\d+$/', $fieldCode), [0, false], true), + ARRAY_FILTER_USE_KEY + ); $this->assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( $fields, ProductServiceItemResult::class diff --git a/tests/Integration/Services/Catalog/Product/Sku/Result/SkuItemResultTest.php b/tests/Integration/Services/Catalog/Product/Sku/Result/SkuItemResultTest.php index ce6b4df0..e467252a 100644 --- a/tests/Integration/Services/Catalog/Product/Sku/Result/SkuItemResultTest.php +++ b/tests/Integration/Services/Catalog/Product/Sku/Result/SkuItemResultTest.php @@ -100,7 +100,13 @@ public function testAllFieldsAreAnnotated(): void #[TestDox('all fields in SkuItemResult have valid type casting in magic getters')] public function testAllFieldsHasValidTypeCastingInMagicGetters(): void { - $fields = $this->skuService->fieldsByFilter($this->iblockId)->getFieldsDescription(); + // priceType is present only in fieldsByFilter response, not in item response, and is not annotated + // dynamic catalog properties (propertyN) vary per portal and are intentionally not annotated + $fields = array_filter( + $this->skuService->fieldsByFilter($this->iblockId)->getFieldsDescription()['sku'], + static fn (string $fieldCode): bool => $fieldCode !== 'priceType' && in_array(preg_match('/^property\d+$/', $fieldCode), [0, false], true), + ARRAY_FILTER_USE_KEY + ); $this->assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( $fields, SkuItemResult::class