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..c3d2488a 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 vendor/bin/phpunit --testsuite integration_tests_scope_catalog + +.PHONY: test-integration-catalog-catalog +test-integration-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 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 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 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 vendor/bin/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..762af872 100644 --- a/src/Services/Catalog/Common/Result/AbstractCatalogItem.php +++ b/src/Services/Catalog/Common/Result/AbstractCatalogItem.php @@ -39,15 +39,21 @@ 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': + 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'; } @@ -67,8 +73,7 @@ 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]; } @@ -78,7 +83,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 +114,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..7d58d2ab --- /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 ?string $height + * @property-read int $id + * @property-read int $iblockId + * @property-read ?array $iblockSection + * @property-read ?int $iblockSectionId + * @property-read ?string $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 ?string $quantity + * @property-read ?string $quantityReserved + * @property-read bool $quantityTrace + * @property-read ?int $recurSchemeLength + * @property-read ?bool $recurSchemeType + * @property-read int $sort + * @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 ?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/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..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 @@ -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..337e5b8a --- /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 ?string $height + * @property-read int $id + * @property-read int $iblockId + * @property-read ?array $iblockSection + * @property-read ?int $iblockSectionId + * @property-read ?string $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 ?string $quantity + * @property-read int $sort + * @property-read bool $subscribe + * @property-read CarbonImmutable $timestampX + * @property-read ProductType $type + * @property-read ?int $vatId + * @property-read bool $vatIncluded + * @property-read ?string $weight + * @property-read ?string $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/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/Catalog/Service/CatalogTest.php b/tests/Integration/Services/Catalog/Catalog/Service/CatalogTest.php index 47e03abb..f98f2987 100644 --- a/tests/Integration/Services/Catalog/Catalog/Service/CatalogTest.php +++ b/tests/Integration/Services/Catalog/Catalog/Service/CatalogTest.php @@ -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(); } -} \ 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..a00b5b6a --- /dev/null +++ b/tests/Integration/Services/Catalog/Product/Offer/Result/OfferItemResultTest.php @@ -0,0 +1,135 @@ + + * + * 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\Fabric; +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; + + private int $offersCatalogIblockId; + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function setUp(): void + { + $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 $catalogItemResult) { + if ($catalogItemResult->productIblockId === null) { + $productCatalog = $catalogItemResult; + } else { + $offersCatalog = $catalogItemResult; + } + } + + $this->offersCatalogIblockId = $offersCatalog->iblockId; + + $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 => in_array(preg_match('/^property\d+$/', $fieldCode), [0, false], true) + ); + + $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 + { + // 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/Offer/Service/OfferTest.php b/tests/Integration/Services/Catalog/Product/Offer/Service/OfferTest.php new file mode 100644 index 00000000..a99e5c51 --- /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\Fabric; +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 = Fabric::getServiceBuilder()->getCatalogScope()->productOffer(); + $this->skuService = Fabric::getServiceBuilder()->getCatalogScope()->productSku(); + $this->catalogService = Fabric::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; + + $offerResult = $this->offerService->add([ + 'iblockId' => $offersCatalog->iblockId, + 'name' => sprintf('test offer %s', time()), + 'parentId' => $skuId, + ]); + $offerId = $offerResult->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); + + $offersResult = $this->offerService->list( + ['id', 'iblockId'], + ['id' => $offerId, 'iblockId' => $offersCatalog->iblockId] + ); + $this->assertCount(1, $offersResult->getOffers()); + + $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 new file mode 100644 index 00000000..81888685 --- /dev/null +++ b/tests/Integration/Services/Catalog/Product/ProductService/Result/ProductServiceItemResultTest.php @@ -0,0 +1,110 @@ + + * + * 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\Fabric; +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; + + private int $iblockId; + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function setUp(): void + { + $serviceBuilder = Fabric::getServiceBuilder(); + $this->productServiceScope = $serviceBuilder->getCatalogScope()->productService(); + $catalogService = $serviceBuilder->getCatalogScope()->catalog(); + $this->iblockId = $catalogService->list([], [], [], 1)->getCatalogs()[0]->iblockId; + + $this->serviceId = $this->productServiceScope->add([ + 'iblockId' => $this->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 => in_array(preg_match('/^property\d+$/', $fieldCode), [0, false], true) + ); + + $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 + { + // 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/ProductService/Service/ProductServiceTest.php b/tests/Integration/Services/Catalog/Product/ProductService/Service/ProductServiceTest.php new file mode 100644 index 00000000..a4384975 --- /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\Fabric; +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 = Fabric::getServiceBuilder()->getCatalogScope()->productService(); + $this->catalogService = Fabric::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; + + $productServiceResult = $this->productServiceScope->add([ + 'iblockId' => $iblockId, + 'name' => sprintf('test service %s', time()), + ]); + $serviceId = $productServiceResult->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); + + $productServicesResult = $this->productServiceScope->list( + ['id', 'iblockId'], + ['id' => $serviceId, 'iblockId' => $iblockId] + ); + $this->assertCount(1, $productServicesResult->getProductServices()); + + $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 3e514c0f..817baa61 100644 --- a/tests/Integration/Services/Catalog/Product/Service/ProductTest.php +++ b/tests/Integration/Services/Catalog/Product/Service/ProductTest.php @@ -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 = [ @@ -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(); } -} \ 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..e467252a --- /dev/null +++ b/tests/Integration/Services/Catalog/Product/Sku/Result/SkuItemResultTest.php @@ -0,0 +1,115 @@ + + * + * 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\Fabric; +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 = Fabric::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 + { + // 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 + ); + } +} 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..7f23622e --- /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\Fabric; +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 = Fabric::getServiceBuilder()->getCatalogScope()->productSku(); + $this->catalogService = Fabric::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; + + $skuResult = $this->skuService->add([ + 'iblockId' => $iblockId, + 'name' => sprintf('test sku %s', time()), + ]); + $skuId = $skuResult->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); + + $skusResult = $this->skuService->list( + ['id', 'iblockId'], + ['id' => $skuId, 'iblockId' => $iblockId] + ); + $this->assertCount(1, $skusResult->getSkus()); + + $fieldsResult = $this->skuService->fieldsByFilter($iblockId); + $this->assertIsArray($fieldsResult->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') + ); + } +}