From 492c48503ab344742f123e6004536850fab5142d Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Tue, 4 Aug 2026 19:36:14 +0400 Subject: [PATCH 1/3] Add catalog.document and catalog.document.element methods --- .php-cs-fixer.php | 1 + CHANGELOG.md | 20 ++ Makefile | 14 + phpunit.xml.dist | 12 + .../Catalog/Result/CatalogItemResult.php | 2 +- .../Catalog/Catalog/Result/CatalogResult.php | 2 +- .../Catalog/Catalog/Result/CatalogsResult.php | 2 +- .../Catalog/Catalog/Service/Catalog.php | 2 +- .../Catalog/CatalogServiceBuilder.php | 35 ++- src/Services/Catalog/Common/ProductType.php | 2 +- .../Common/Result/AbstractCatalogItem.php | 2 +- src/Services/Catalog/Document/Batch.php | 108 ++++++++ .../Result/DocumentAddedBatchResult.php | 33 +++ .../Document/Result/DocumentFieldsResult.php | 29 +++ .../Document/Result/DocumentItemResult.php | 40 +++ .../Result/DocumentModeStatusResult.php | 30 +++ .../Document/Result/DocumentResult.php | 28 ++ .../Result/DocumentUpdatedBatchResult.php | 33 +++ .../Document/Result/DocumentsResult.php | 34 +++ .../Catalog/Document/Service/Batch.php | 103 ++++++++ .../Catalog/Document/Service/Document.php | 246 ++++++++++++++++++ .../Catalog/DocumentElement/Batch.php | 108 ++++++++ .../DocumentElementAddedBatchResult.php | 33 +++ .../Result/DocumentElementFieldsResult.php | 29 +++ .../Result/DocumentElementItemResult.php | 29 +++ .../Result/DocumentElementResult.php | 28 ++ .../DocumentElementUpdatedBatchResult.php | 33 +++ .../Result/DocumentElementsResult.php | 34 +++ .../Catalog/DocumentElement/Service/Batch.php | 103 ++++++++ .../Service/DocumentElement.php | 133 ++++++++++ .../Product/Result/ProductItemResult.php | 2 +- .../Catalog/Product/Result/ProductResult.php | 2 +- .../Catalog/Product/Result/ProductsResult.php | 2 +- .../Catalog/Product/Service/Batch.php | 6 +- .../Catalog/Product/Service/Product.php | 10 +- .../Result/DocumentItemResultTest.php | 68 +++++ .../Catalog/Document/Service/BatchTest.php | 73 ++++++ .../Catalog/Document/Service/DocumentTest.php | 222 ++++++++++++++++ .../Result/DocumentElementItemResultTest.php | 105 ++++++++ .../DocumentElement/Service/BatchTest.php | 121 +++++++++ .../Service/DocumentElementTest.php | 120 +++++++++ .../Catalog/Document/Service/DocumentTest.php | 134 ++++++++++ .../Service/DocumentElementTest.php | 96 +++++++ 43 files changed, 2251 insertions(+), 18 deletions(-) create mode 100644 src/Services/Catalog/Document/Batch.php create mode 100644 src/Services/Catalog/Document/Result/DocumentAddedBatchResult.php create mode 100644 src/Services/Catalog/Document/Result/DocumentFieldsResult.php create mode 100644 src/Services/Catalog/Document/Result/DocumentItemResult.php create mode 100644 src/Services/Catalog/Document/Result/DocumentModeStatusResult.php create mode 100644 src/Services/Catalog/Document/Result/DocumentResult.php create mode 100644 src/Services/Catalog/Document/Result/DocumentUpdatedBatchResult.php create mode 100644 src/Services/Catalog/Document/Result/DocumentsResult.php create mode 100644 src/Services/Catalog/Document/Service/Batch.php create mode 100644 src/Services/Catalog/Document/Service/Document.php create mode 100644 src/Services/Catalog/DocumentElement/Batch.php create mode 100644 src/Services/Catalog/DocumentElement/Result/DocumentElementAddedBatchResult.php create mode 100644 src/Services/Catalog/DocumentElement/Result/DocumentElementFieldsResult.php create mode 100644 src/Services/Catalog/DocumentElement/Result/DocumentElementItemResult.php create mode 100644 src/Services/Catalog/DocumentElement/Result/DocumentElementResult.php create mode 100644 src/Services/Catalog/DocumentElement/Result/DocumentElementUpdatedBatchResult.php create mode 100644 src/Services/Catalog/DocumentElement/Result/DocumentElementsResult.php create mode 100644 src/Services/Catalog/DocumentElement/Service/Batch.php create mode 100644 src/Services/Catalog/DocumentElement/Service/DocumentElement.php create mode 100644 tests/Integration/Services/Catalog/Document/Result/DocumentItemResultTest.php create mode 100644 tests/Integration/Services/Catalog/Document/Service/BatchTest.php create mode 100644 tests/Integration/Services/Catalog/Document/Service/DocumentTest.php create mode 100644 tests/Integration/Services/Catalog/DocumentElement/Result/DocumentElementItemResultTest.php create mode 100644 tests/Integration/Services/Catalog/DocumentElement/Service/BatchTest.php create mode 100644 tests/Integration/Services/Catalog/DocumentElement/Service/DocumentElementTest.php create mode 100644 tests/Unit/Services/Catalog/Document/Service/DocumentTest.php create mode 100644 tests/Unit/Services/Catalog/DocumentElement/Service/DocumentElementTest.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..42735db9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,26 @@ ### Added +- Added service `Services\Catalog\Document` with support methods, + see [catalog.document.* methods](https://apidocs.bitrix24.com/api-reference/catalog/document/index.html) ([#559](https://github.com/bitrix24/b24phpsdk/issues/559)): + - `add` creates a new warehouse accounting document, with batch calls support + - `update` updates an existing document, with batch calls support + - `list` gets the list of documents + - `delete` deletes a document, with batch calls support + - `deleteList` deletes a group of documents + - `conduct` conducts (activates) a document + - `conductList` conducts a group of documents + - `cancel` cancels conducting of a document + - `cancelList` cancels conducting of a group of documents + - `getFields` returns the description of document fields + - `modeStatus` checks whether warehouse accounting mode is enabled +- Added service `Services\Catalog\DocumentElement` with support methods, + see [catalog.document.element.* methods](https://apidocs.bitrix24.com/api-reference/catalog/document/document-element/index.html) ([#559](https://github.com/bitrix24/b24phpsdk/issues/559)): + - `add` adds a product line item to a warehouse accounting document, with batch calls support + - `update` updates a document line item, with batch calls support + - `list` gets the list of document line items + - `delete` deletes a document line item, with batch calls support + - `getFields` returns the description of document element fields - Added service `Services\Landing\Site\Service\Site` with support methods, see [landing.site.* methods](https://github.com/bitrix24/b24phpsdk/issues/267): - `add` adds a site diff --git a/Makefile b/Makefile index c2fc4a9a..a0fcaf6f 100644 --- a/Makefile +++ b/Makefile @@ -492,6 +492,20 @@ test-integration-landing-role: test-integration-landing-repowidget: docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_landing_repowidget +.PHONY: test-integration-catalog-document +test-integration-catalog-document: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_catalog_document +.PHONY: test-integration-catalog-document-annotations +test-integration-catalog-document-annotations: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_catalog_document_annotations + +.PHONY: test-integration-catalog-document-element +test-integration-catalog-document-element: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_catalog_document_element +.PHONY: test-integration-catalog-document-element-annotations +test-integration-catalog-document-element-annotations: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_catalog_document_element_annotations + # work dev environment .PHONY: php-dev-server-up php-dev-server-up: diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d0187ae5..97216df3 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -262,6 +262,18 @@ ./tests/Integration/Services/Landing/RepoWidget/ + + ./tests/Integration/Services/Catalog/Document/Service/ + + + ./tests/Integration/Services/Catalog/Document/Result/DocumentItemResultTest.php + + + ./tests/Integration/Services/Catalog/DocumentElement/Service/ + + + ./tests/Integration/Services/Catalog/DocumentElement/Result/DocumentElementItemResultTest.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..f721c3bb 100644 --- a/src/Services/Catalog/Catalog/Result/CatalogsResult.php +++ b/src/Services/Catalog/Catalog/Result/CatalogsResult.php @@ -32,4 +32,4 @@ public function getCatalogs(): array return $res; } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Catalog/Service/Catalog.php b/src/Services/Catalog/Catalog/Service/Catalog.php index d9cb55fe..0da76ea3 100644 --- a/src/Services/Catalog/Catalog/Service/Catalog.php +++ b/src/Services/Catalog/Catalog/Service/Catalog.php @@ -82,4 +82,4 @@ public function fields(): FieldsResult { return new FieldsResult($this->core->call('catalog.catalog.getFields')); } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/CatalogServiceBuilder.php b/src/Services/Catalog/CatalogServiceBuilder.php index 57c91b88..f2d2ef4a 100644 --- a/src/Services/Catalog/CatalogServiceBuilder.php +++ b/src/Services/Catalog/CatalogServiceBuilder.php @@ -17,6 +17,7 @@ use Bitrix24\SDK\Core\Credentials\Scope; use Bitrix24\SDK\Services\AbstractServiceBuilder; use Bitrix24\SDK\Services\Catalog; + #[ApiServiceBuilderMetadata(new Scope(['catalog']))] class CatalogServiceBuilder extends AbstractServiceBuilder { @@ -44,4 +45,36 @@ public function catalog(): Catalog\Catalog\Service\Catalog return $this->serviceCache[__METHOD__]; } -} \ No newline at end of file + + public function document(): Catalog\Document\Service\Document + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Catalog\Document\Service\Document( + new Catalog\Document\Service\Batch( + new Catalog\Document\Batch($this->core, $this->log), + $this->log + ), + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } + + public function documentElement(): Catalog\DocumentElement\Service\DocumentElement + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Catalog\DocumentElement\Service\DocumentElement( + new Catalog\DocumentElement\Service\Batch( + new Catalog\DocumentElement\Batch($this->core, $this->log), + $this->log + ), + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } +} diff --git a/src/Services/Catalog/Common/ProductType.php b/src/Services/Catalog/Common/ProductType.php index 0bf8ef09..ad568a47 100644 --- a/src/Services/Catalog/Common/ProductType.php +++ b/src/Services/Catalog/Common/ProductType.php @@ -20,4 +20,4 @@ enum ProductType: int case SKU = 3; case productOffer = 4; case genericOffer = 5; -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Common/Result/AbstractCatalogItem.php b/src/Services/Catalog/Common/Result/AbstractCatalogItem.php index e2929d73..9761daed 100644 --- a/src/Services/Catalog/Common/Result/AbstractCatalogItem.php +++ b/src/Services/Catalog/Common/Result/AbstractCatalogItem.php @@ -109,4 +109,4 @@ protected function getKeyWithUserfieldByFieldName(string $fieldName) return $this->$fieldName; } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Document/Batch.php b/src/Services/Catalog/Document/Batch.php new file mode 100644 index 00000000..7eed45fe --- /dev/null +++ b/src/Services/Catalog/Document/Batch.php @@ -0,0 +1,108 @@ + + * + * 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\Document; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Response\DTO\ResponseData; +use Generator; + +/** + * Class Batch + * + * Overrides base Batch to handle parameter naming differences in catalog.document.* REST methods: + * - delete uses lowercase 'id' instead of 'ID' + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-delete.html + * @see https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-list.html + */ +class Batch extends \Bitrix24\SDK\Core\Batch +{ + /** + * Determines the ID key — lowercase 'id' for catalog document + */ + #[\Override] + protected function determineKeyId(string $apiMethod, ?array $additionalParameters): string + { + return 'id'; + } + + /** + * Delete entity items with batch call using lowercase 'id' parameter + * + * @param int[] $entityItemId + * @param array|null $additionalParameters + * + * @return Generator|ResponseData[] + * @throws BaseException + */ + #[\Override] + public function deleteEntityItems( + string $apiMethod, + array $entityItemId, + ?array $additionalParameters = null + ): Generator { + $this->logger->debug( + 'deleteEntityItems.start', + [ + 'apiMethod' => $apiMethod, + 'entityItems' => $entityItemId, + 'additionalParameters' => $additionalParameters, + ] + ); + + try { + $this->clearCommands(); + foreach ($entityItemId as $cnt => $itemId) { + if (!is_int($itemId)) { + throw new InvalidArgumentException( + sprintf( + 'invalid type «%s» of document id «%s» at position %s, document id must be integer type', + gettype($itemId), + $itemId, + $cnt + ) + ); + } + + $this->registerCommand($apiMethod, ['id' => $itemId]); + } + + foreach ($this->getTraversable(true) as $cnt => $deletedItemResult) { + yield $cnt => $deletedItemResult; + } + } catch (InvalidArgumentException $exception) { + $errorMessage = sprintf('batch delete document items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + throw $exception; + } catch (\Throwable $exception) { + $errorMessage = sprintf('batch delete document items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + + throw new BaseException($errorMessage, $exception->getCode(), $exception); + } + + $this->logger->debug('deleteEntityItems.finish'); + } +} diff --git a/src/Services/Catalog/Document/Result/DocumentAddedBatchResult.php b/src/Services/Catalog/Document/Result/DocumentAddedBatchResult.php new file mode 100644 index 00000000..ba4eb039 --- /dev/null +++ b/src/Services/Catalog/Document/Result/DocumentAddedBatchResult.php @@ -0,0 +1,33 @@ + + * + * 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\Document\Result; + +use Bitrix24\SDK\Core\Response\DTO\ResponseData; + +class DocumentAddedBatchResult +{ + public function __construct(private readonly ResponseData $responseData) + { + } + + public function getResponseData(): ResponseData + { + return $this->responseData; + } + + public function document(): DocumentItemResult + { + return new DocumentItemResult($this->responseData->getResult()['document']); + } +} diff --git a/src/Services/Catalog/Document/Result/DocumentFieldsResult.php b/src/Services/Catalog/Document/Result/DocumentFieldsResult.php new file mode 100644 index 00000000..10b5bd72 --- /dev/null +++ b/src/Services/Catalog/Document/Result/DocumentFieldsResult.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\Document\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class DocumentFieldsResult extends AbstractResult +{ + /** + * @return array> + * @throws BaseException + */ + public function getFieldsDescription(): array + { + return $this->getCoreResponse()->getResponseData()->getResult()['document']; + } +} diff --git a/src/Services/Catalog/Document/Result/DocumentItemResult.php b/src/Services/Catalog/Document/Result/DocumentItemResult.php new file mode 100644 index 00000000..742e4be1 --- /dev/null +++ b/src/Services/Catalog/Document/Result/DocumentItemResult.php @@ -0,0 +1,40 @@ + + * + * 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\Document\Result; + +use Bitrix24\SDK\Core\Result\AbstractAnnotatedItem; +use Carbon\CarbonImmutable; + +/** + * @property-read int $id + * @property-read string $docType + * @property-read string $currency + * @property-read int $responsibleId + * @property-read string|null $siteId + * @property-read CarbonImmutable|null $dateDocument + * @property-read CarbonImmutable|null $dateCreate + * @property-read CarbonImmutable|null $dateModify + * @property-read CarbonImmutable|null $dateStatus + * @property-read string|null $title + * @property-read string|null $commentary + * @property-read float|null $total + * @property-read string|null $docNumber + * @property-read int|null $createdBy + * @property-read int|null $modifiedBy + * @property-read string|null $status + * @property-read int|null $statusBy + */ +class DocumentItemResult extends AbstractAnnotatedItem +{ +} diff --git a/src/Services/Catalog/Document/Result/DocumentModeStatusResult.php b/src/Services/Catalog/Document/Result/DocumentModeStatusResult.php new file mode 100644 index 00000000..075c6740 --- /dev/null +++ b/src/Services/Catalog/Document/Result/DocumentModeStatusResult.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\Document\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class DocumentModeStatusResult extends AbstractResult +{ + /** + * Returns true if warehouse accounting mode is enabled ('Y'), false otherwise ('N') + * + * @throws BaseException + */ + public function isEnabled(): bool + { + return $this->getCoreResponse()->getResponseData()->getResult()[0] === 'Y'; + } +} diff --git a/src/Services/Catalog/Document/Result/DocumentResult.php b/src/Services/Catalog/Document/Result/DocumentResult.php new file mode 100644 index 00000000..47141ce8 --- /dev/null +++ b/src/Services/Catalog/Document/Result/DocumentResult.php @@ -0,0 +1,28 @@ + + * + * 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\Document\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class DocumentResult extends AbstractResult +{ + /** + * @throws BaseException + */ + public function document(): DocumentItemResult + { + return new DocumentItemResult($this->getCoreResponse()->getResponseData()->getResult()['document']); + } +} diff --git a/src/Services/Catalog/Document/Result/DocumentUpdatedBatchResult.php b/src/Services/Catalog/Document/Result/DocumentUpdatedBatchResult.php new file mode 100644 index 00000000..a5d11ff0 --- /dev/null +++ b/src/Services/Catalog/Document/Result/DocumentUpdatedBatchResult.php @@ -0,0 +1,33 @@ + + * + * 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\Document\Result; + +use Bitrix24\SDK\Core\Response\DTO\ResponseData; + +class DocumentUpdatedBatchResult +{ + public function __construct(private readonly ResponseData $responseData) + { + } + + public function getResponseData(): ResponseData + { + return $this->responseData; + } + + public function document(): DocumentItemResult + { + return new DocumentItemResult($this->responseData->getResult()['document']); + } +} diff --git a/src/Services/Catalog/Document/Result/DocumentsResult.php b/src/Services/Catalog/Document/Result/DocumentsResult.php new file mode 100644 index 00000000..fa578866 --- /dev/null +++ b/src/Services/Catalog/Document/Result/DocumentsResult.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\Document\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class DocumentsResult extends AbstractResult +{ + /** + * @return DocumentItemResult[] + * @throws BaseException + */ + public function getDocuments(): array + { + $result = $this->getCoreResponse()->getResponseData()->getResult(); + + return array_map( + static fn (array $item): DocumentItemResult => new DocumentItemResult($item), + $result['documents'] ?? [] + ); + } +} diff --git a/src/Services/Catalog/Document/Service/Batch.php b/src/Services/Catalog/Document/Service/Batch.php new file mode 100644 index 00000000..38a54e46 --- /dev/null +++ b/src/Services/Catalog/Document/Service/Batch.php @@ -0,0 +1,103 @@ + + * + * 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\Document\Service; + +use Bitrix24\SDK\Attributes\ApiBatchMethodMetadata; +use Bitrix24\SDK\Attributes\ApiBatchServiceMetadata; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\DeletedItemBatchResult; +use Bitrix24\SDK\Services\Catalog\Document; +use Bitrix24\SDK\Services\Catalog\Document\Result\DocumentAddedBatchResult; +use Bitrix24\SDK\Services\Catalog\Document\Result\DocumentUpdatedBatchResult; +use Generator; +use Psr\Log\LoggerInterface; + +#[ApiBatchServiceMetadata(new Scope(['catalog']))] +class Batch +{ + public function __construct(protected Document\Batch $batch, protected LoggerInterface $log) + { + } + + /** + * Batch adding warehouse accounting documents + * + * @param array $documents + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'catalog.document.add', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-add.html', + 'Batch adding warehouse accounting documents' + )] + public function add(array $documents): Generator + { + $items = []; + foreach ($documents as $document) { + $items[] = ['fields' => $document]; + } + + foreach ($this->batch->addEntityItems('catalog.document.add', $items) as $key => $item) { + yield $key => new DocumentAddedBatchResult($item); + } + } + + /** + * Batch delete warehouse accounting documents + * + * @param int[] $documentId + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'catalog.document.delete', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-delete.html', + 'Batch delete warehouse accounting documents' + )] + public function delete(array $documentId): Generator + { + foreach ($this->batch->deleteEntityItems('catalog.document.delete', $documentId) as $key => $item) { + yield $key => new DeletedItemBatchResult($item); + } + } + + /** + * Batch update warehouse accounting documents + * + * @param array $documents keyed by document id + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'catalog.document.update', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-update.html', + 'Batch update warehouse accounting documents' + )] + public function update(array $documents): Generator + { + $items = []; + foreach ($documents as $id => $document) { + $items[$id] = ['fields' => $document]; + } + + foreach ($this->batch->updateEntityItems('catalog.document.update', $items) as $key => $item) { + yield $key => new DocumentUpdatedBatchResult($item); + } + } +} diff --git a/src/Services/Catalog/Document/Service/Document.php b/src/Services/Catalog/Document/Service/Document.php new file mode 100644 index 00000000..63b00738 --- /dev/null +++ b/src/Services/Catalog/Document/Service/Document.php @@ -0,0 +1,246 @@ + + * + * 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\Document\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\Result\DeletedItemResult; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\Catalog\Document\Result\DocumentFieldsResult; +use Bitrix24\SDK\Services\Catalog\Document\Result\DocumentModeStatusResult; +use Bitrix24\SDK\Services\Catalog\Document\Result\DocumentResult; +use Bitrix24\SDK\Services\Catalog\Document\Result\DocumentsResult; +use Psr\Log\LoggerInterface; + +#[ApiServiceMetadata(new Scope(['catalog']))] +class Document extends AbstractService +{ + public function __construct(public Batch $batch, CoreInterface $core, LoggerInterface $logger) + { + parent::__construct($core, $logger); + } + + /** + * Adds a new warehouse accounting document + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-add.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.document.add', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-add.html', + 'Adds a new warehouse accounting document' + )] + public function add(array $fields): DocumentResult + { + return new DocumentResult($this->core->call('catalog.document.add', ['fields' => $fields])); + } + + /** + * Updates a warehouse accounting document by its identifier + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-update.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.document.update', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-update.html', + 'Updates a warehouse accounting document by its identifier' + )] + public function update(int $id, array $fields): DocumentResult + { + return new DocumentResult($this->core->call('catalog.document.update', ['id' => $id, 'fields' => $fields])); + } + + /** + * Returns a list of warehouse accounting documents by filter + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-list.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.document.list', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-list.html', + 'Returns a list of warehouse accounting documents by filter' + )] + public function list(array $select = [], array $filter = []): DocumentsResult + { + return new DocumentsResult( + $this->core->call( + 'catalog.document.list', + ['select' => $select, 'filter' => $filter] + ) + ); + } + + /** + * Deletes a warehouse accounting document by identifier + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-delete.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.document.delete', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-delete.html', + 'Deletes a warehouse accounting document by identifier' + )] + public function delete(int $id): DeletedItemResult + { + return new DeletedItemResult($this->core->call('catalog.document.delete', ['id' => $id])); + } + + /** + * Deletes a group of warehouse accounting documents by identifiers + * + * @param int[] $documentIds + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-delete-list.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.document.deleteList', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-delete-list.html', + 'Deletes a group of warehouse accounting documents by identifiers' + )] + public function deleteList(array $documentIds): DeletedItemResult + { + return new DeletedItemResult($this->core->call('catalog.document.deleteList', ['documentIds' => $documentIds])); + } + + /** + * Conducts a warehouse accounting document, updating stock balances + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-conduct.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.document.conduct', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-conduct.html', + 'Conducts a warehouse accounting document, updating stock balances' + )] + public function conduct(int $id): DeletedItemResult + { + return new DeletedItemResult($this->core->call('catalog.document.conduct', ['id' => $id])); + } + + /** + * Conducts a group of warehouse accounting documents, updating stock balances + * + * @param int[] $documentIds + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-conduct-list.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.document.conductList', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-conduct-list.html', + 'Conducts a group of warehouse accounting documents, updating stock balances' + )] + public function conductList(array $documentIds): DeletedItemResult + { + return new DeletedItemResult($this->core->call('catalog.document.conductList', ['documentIds' => $documentIds])); + } + + /** + * Cancels conducting of a warehouse accounting document, updating stock balances + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-cancel.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.document.cancel', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-cancel.html', + 'Cancels conducting of a warehouse accounting document, updating stock balances' + )] + public function cancel(int $id): DeletedItemResult + { + return new DeletedItemResult($this->core->call('catalog.document.cancel', ['id' => $id])); + } + + /** + * Cancels conducting of a group of warehouse accounting documents, updating stock balances + * + * @param int[] $documentIds + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-cancel-list.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.document.cancelList', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-cancel-list.html', + 'Cancels conducting of a group of warehouse accounting documents, updating stock balances' + )] + public function cancelList(array $documentIds): DeletedItemResult + { + return new DeletedItemResult($this->core->call('catalog.document.cancelList', ['documentIds' => $documentIds])); + } + + /** + * Returns the fields of a warehouse accounting document + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-get-fields.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.document.getFields', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-get-fields.html', + 'Returns the fields of a warehouse accounting document' + )] + public function getFields(): DocumentFieldsResult + { + return new DocumentFieldsResult($this->core->call('catalog.document.getFields')); + } + + /** + * Checks whether warehouse accounting mode is enabled + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-mode-status.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.document.mode.status', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/catalog-document-mode-status.html', + 'Checks whether warehouse accounting mode is enabled' + )] + public function modeStatus(): DocumentModeStatusResult + { + return new DocumentModeStatusResult($this->core->call('catalog.document.mode.status')); + } +} diff --git a/src/Services/Catalog/DocumentElement/Batch.php b/src/Services/Catalog/DocumentElement/Batch.php new file mode 100644 index 00000000..ad36f7a7 --- /dev/null +++ b/src/Services/Catalog/DocumentElement/Batch.php @@ -0,0 +1,108 @@ + + * + * 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\DocumentElement; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Response\DTO\ResponseData; +use Generator; + +/** + * Class Batch + * + * Overrides base Batch to handle parameter naming differences in catalog.document.element.* REST methods: + * - delete uses lowercase 'id' instead of 'ID' + * + * @see https://apidocs.bitrix24.com/api-reference/catalog/document/document-element/catalog-document-element-delete.html + * @see https://apidocs.bitrix24.com/api-reference/catalog/document/document-element/catalog-document-element-list.html + */ +class Batch extends \Bitrix24\SDK\Core\Batch +{ + /** + * Determines the ID key — lowercase 'id' for catalog document element + */ + #[\Override] + protected function determineKeyId(string $apiMethod, ?array $additionalParameters): string + { + return 'id'; + } + + /** + * Delete entity items with batch call using lowercase 'id' parameter + * + * @param int[] $entityItemId + * @param array|null $additionalParameters + * + * @return Generator|ResponseData[] + * @throws BaseException + */ + #[\Override] + public function deleteEntityItems( + string $apiMethod, + array $entityItemId, + ?array $additionalParameters = null + ): Generator { + $this->logger->debug( + 'deleteEntityItems.start', + [ + 'apiMethod' => $apiMethod, + 'entityItems' => $entityItemId, + 'additionalParameters' => $additionalParameters, + ] + ); + + try { + $this->clearCommands(); + foreach ($entityItemId as $cnt => $itemId) { + if (!is_int($itemId)) { + throw new InvalidArgumentException( + sprintf( + 'invalid type «%s» of document element id «%s» at position %s, document element id must be integer type', + gettype($itemId), + $itemId, + $cnt + ) + ); + } + + $this->registerCommand($apiMethod, ['id' => $itemId]); + } + + foreach ($this->getTraversable(true) as $cnt => $deletedItemResult) { + yield $cnt => $deletedItemResult; + } + } catch (InvalidArgumentException $exception) { + $errorMessage = sprintf('batch delete document element items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + throw $exception; + } catch (\Throwable $exception) { + $errorMessage = sprintf('batch delete document element items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + + throw new BaseException($errorMessage, $exception->getCode(), $exception); + } + + $this->logger->debug('deleteEntityItems.finish'); + } +} diff --git a/src/Services/Catalog/DocumentElement/Result/DocumentElementAddedBatchResult.php b/src/Services/Catalog/DocumentElement/Result/DocumentElementAddedBatchResult.php new file mode 100644 index 00000000..d48c331e --- /dev/null +++ b/src/Services/Catalog/DocumentElement/Result/DocumentElementAddedBatchResult.php @@ -0,0 +1,33 @@ + + * + * 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\DocumentElement\Result; + +use Bitrix24\SDK\Core\Response\DTO\ResponseData; + +class DocumentElementAddedBatchResult +{ + public function __construct(private readonly ResponseData $responseData) + { + } + + public function getResponseData(): ResponseData + { + return $this->responseData; + } + + public function documentElement(): DocumentElementItemResult + { + return new DocumentElementItemResult($this->responseData->getResult()['documentElement']); + } +} diff --git a/src/Services/Catalog/DocumentElement/Result/DocumentElementFieldsResult.php b/src/Services/Catalog/DocumentElement/Result/DocumentElementFieldsResult.php new file mode 100644 index 00000000..45644edd --- /dev/null +++ b/src/Services/Catalog/DocumentElement/Result/DocumentElementFieldsResult.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\DocumentElement\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class DocumentElementFieldsResult extends AbstractResult +{ + /** + * @return array> + * @throws BaseException + */ + public function getFieldsDescription(): array + { + return $this->getCoreResponse()->getResponseData()->getResult()['documentElement']; + } +} diff --git a/src/Services/Catalog/DocumentElement/Result/DocumentElementItemResult.php b/src/Services/Catalog/DocumentElement/Result/DocumentElementItemResult.php new file mode 100644 index 00000000..3c6df0f2 --- /dev/null +++ b/src/Services/Catalog/DocumentElement/Result/DocumentElementItemResult.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Catalog\DocumentElement\Result; + +use Bitrix24\SDK\Core\Result\AbstractAnnotatedItem; + +/** + * @property-read int $id + * @property-read int $docId + * @property-read int $elementId + * @property-read int|null $storeFrom + * @property-read int|null $storeTo + * @property-read float $amount + * @property-read float|null $purchasingPrice + */ +class DocumentElementItemResult extends AbstractAnnotatedItem +{ +} diff --git a/src/Services/Catalog/DocumentElement/Result/DocumentElementResult.php b/src/Services/Catalog/DocumentElement/Result/DocumentElementResult.php new file mode 100644 index 00000000..53d8bb78 --- /dev/null +++ b/src/Services/Catalog/DocumentElement/Result/DocumentElementResult.php @@ -0,0 +1,28 @@ + + * + * 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\DocumentElement\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class DocumentElementResult extends AbstractResult +{ + /** + * @throws BaseException + */ + public function documentElement(): DocumentElementItemResult + { + return new DocumentElementItemResult($this->getCoreResponse()->getResponseData()->getResult()['documentElement']); + } +} diff --git a/src/Services/Catalog/DocumentElement/Result/DocumentElementUpdatedBatchResult.php b/src/Services/Catalog/DocumentElement/Result/DocumentElementUpdatedBatchResult.php new file mode 100644 index 00000000..20b84944 --- /dev/null +++ b/src/Services/Catalog/DocumentElement/Result/DocumentElementUpdatedBatchResult.php @@ -0,0 +1,33 @@ + + * + * 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\DocumentElement\Result; + +use Bitrix24\SDK\Core\Response\DTO\ResponseData; + +class DocumentElementUpdatedBatchResult +{ + public function __construct(private readonly ResponseData $responseData) + { + } + + public function getResponseData(): ResponseData + { + return $this->responseData; + } + + public function documentElement(): DocumentElementItemResult + { + return new DocumentElementItemResult($this->responseData->getResult()['documentElement']); + } +} diff --git a/src/Services/Catalog/DocumentElement/Result/DocumentElementsResult.php b/src/Services/Catalog/DocumentElement/Result/DocumentElementsResult.php new file mode 100644 index 00000000..e78df0e8 --- /dev/null +++ b/src/Services/Catalog/DocumentElement/Result/DocumentElementsResult.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\DocumentElement\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class DocumentElementsResult extends AbstractResult +{ + /** + * @return DocumentElementItemResult[] + * @throws BaseException + */ + public function getDocumentElements(): array + { + $result = $this->getCoreResponse()->getResponseData()->getResult(); + + return array_map( + static fn (array $item): DocumentElementItemResult => new DocumentElementItemResult($item), + $result['documentElements'] ?? [] + ); + } +} diff --git a/src/Services/Catalog/DocumentElement/Service/Batch.php b/src/Services/Catalog/DocumentElement/Service/Batch.php new file mode 100644 index 00000000..1e6cc833 --- /dev/null +++ b/src/Services/Catalog/DocumentElement/Service/Batch.php @@ -0,0 +1,103 @@ + + * + * 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\DocumentElement\Service; + +use Bitrix24\SDK\Attributes\ApiBatchMethodMetadata; +use Bitrix24\SDK\Attributes\ApiBatchServiceMetadata; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\DeletedItemBatchResult; +use Bitrix24\SDK\Services\Catalog\DocumentElement; +use Bitrix24\SDK\Services\Catalog\DocumentElement\Result\DocumentElementAddedBatchResult; +use Bitrix24\SDK\Services\Catalog\DocumentElement\Result\DocumentElementUpdatedBatchResult; +use Generator; +use Psr\Log\LoggerInterface; + +#[ApiBatchServiceMetadata(new Scope(['catalog']))] +class Batch +{ + public function __construct(protected DocumentElement\Batch $batch, protected LoggerInterface $log) + { + } + + /** + * Batch adding warehouse accounting document line items + * + * @param array $documentElements + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'catalog.document.element.add', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/document-element/catalog-document-element-add.html', + 'Batch adding warehouse accounting document line items' + )] + public function add(array $documentElements): Generator + { + $items = []; + foreach ($documentElements as $documentElement) { + $items[] = ['fields' => $documentElement]; + } + + foreach ($this->batch->addEntityItems('catalog.document.element.add', $items) as $key => $item) { + yield $key => new DocumentElementAddedBatchResult($item); + } + } + + /** + * Batch delete warehouse accounting document line items + * + * @param int[] $documentElementId + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'catalog.document.element.delete', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/document-element/catalog-document-element-delete.html', + 'Batch delete warehouse accounting document line items' + )] + public function delete(array $documentElementId): Generator + { + foreach ($this->batch->deleteEntityItems('catalog.document.element.delete', $documentElementId) as $key => $item) { + yield $key => new DeletedItemBatchResult($item); + } + } + + /** + * Batch update warehouse accounting document line items + * + * @param array $documentElements keyed by document element id + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'catalog.document.element.update', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/document-element/catalog-document-element-update.html', + 'Batch update warehouse accounting document line items' + )] + public function update(array $documentElements): Generator + { + $items = []; + foreach ($documentElements as $id => $documentElement) { + $items[$id] = ['fields' => $documentElement]; + } + + foreach ($this->batch->updateEntityItems('catalog.document.element.update', $items) as $key => $item) { + yield $key => new DocumentElementUpdatedBatchResult($item); + } + } +} diff --git a/src/Services/Catalog/DocumentElement/Service/DocumentElement.php b/src/Services/Catalog/DocumentElement/Service/DocumentElement.php new file mode 100644 index 00000000..dfb673be --- /dev/null +++ b/src/Services/Catalog/DocumentElement/Service/DocumentElement.php @@ -0,0 +1,133 @@ + + * + * 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\DocumentElement\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\Result\DeletedItemResult; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\Catalog\DocumentElement\Result\DocumentElementFieldsResult; +use Bitrix24\SDK\Services\Catalog\DocumentElement\Result\DocumentElementResult; +use Bitrix24\SDK\Services\Catalog\DocumentElement\Result\DocumentElementsResult; +use Psr\Log\LoggerInterface; + +#[ApiServiceMetadata(new Scope(['catalog']))] +class DocumentElement extends AbstractService +{ + public function __construct(public Batch $batch, CoreInterface $core, LoggerInterface $logger) + { + parent::__construct($core, $logger); + } + + /** + * Adds a product line item to a warehouse accounting document + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/document/document-element/catalog-document-element-add.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.document.element.add', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/document-element/catalog-document-element-add.html', + 'Adds a product line item to a warehouse accounting document' + )] + public function add(array $fields): DocumentElementResult + { + return new DocumentElementResult($this->core->call('catalog.document.element.add', ['fields' => $fields])); + } + + /** + * Updates a warehouse accounting document line item by its identifier + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/document/document-element/catalog-document-element-update.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.document.element.update', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/document-element/catalog-document-element-update.html', + 'Updates a warehouse accounting document line item by its identifier' + )] + public function update(int $id, array $fields): DocumentElementResult + { + return new DocumentElementResult( + $this->core->call('catalog.document.element.update', ['id' => $id, 'fields' => $fields]) + ); + } + + /** + * Returns a list of warehouse accounting document line items by filter + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/document/document-element/catalog-document-element-list.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.document.element.list', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/document-element/catalog-document-element-list.html', + 'Returns a list of warehouse accounting document line items by filter' + )] + public function list(array $select = [], array $filter = [], array $order = []): DocumentElementsResult + { + return new DocumentElementsResult( + $this->core->call( + 'catalog.document.element.list', + ['select' => $select, 'filter' => $filter, 'order' => $order] + ) + ); + } + + /** + * Deletes a warehouse accounting document line item by identifier + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/document/document-element/catalog-document-element-delete.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.document.element.delete', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/document-element/catalog-document-element-delete.html', + 'Deletes a warehouse accounting document line item by identifier' + )] + public function delete(int $id): DeletedItemResult + { + return new DeletedItemResult($this->core->call('catalog.document.element.delete', ['id' => $id])); + } + + /** + * Returns the fields of a warehouse accounting document line item + * + * @link https://apidocs.bitrix24.com/api-reference/catalog/document/document-element/catalog-document-element-get-fields.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'catalog.document.element.getFields', + 'https://apidocs.bitrix24.com/api-reference/catalog/document/document-element/catalog-document-element-get-fields.html', + 'Returns the fields of a warehouse accounting document line item' + )] + public function getFields(): DocumentElementFieldsResult + { + return new DocumentElementFieldsResult($this->core->call('catalog.document.element.getFields')); + } +} diff --git a/src/Services/Catalog/Product/Result/ProductItemResult.php b/src/Services/Catalog/Product/Result/ProductItemResult.php index c576d9b7..58c80851 100644 --- a/src/Services/Catalog/Product/Result/ProductItemResult.php +++ b/src/Services/Catalog/Product/Result/ProductItemResult.php @@ -52,4 +52,4 @@ */ class ProductItemResult extends AbstractCatalogItem { -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Product/Result/ProductResult.php b/src/Services/Catalog/Product/Result/ProductResult.php index 2751fda8..fc83fa8c 100644 --- a/src/Services/Catalog/Product/Result/ProductResult.php +++ b/src/Services/Catalog/Product/Result/ProductResult.php @@ -26,4 +26,4 @@ public function product(): ProductItemResult return new ProductItemResult($this->getCoreResponse()->getResponseData()->getResult()['product']); } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Product/Result/ProductsResult.php b/src/Services/Catalog/Product/Result/ProductsResult.php index aa644fa3..268d6d8f 100644 --- a/src/Services/Catalog/Product/Result/ProductsResult.php +++ b/src/Services/Catalog/Product/Result/ProductsResult.php @@ -31,4 +31,4 @@ public function getProducts(): array return $res; } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Product/Service/Batch.php b/src/Services/Catalog/Product/Service/Batch.php index 45dddcb7..42970546 100644 --- a/src/Services/Catalog/Product/Service/Batch.php +++ b/src/Services/Catalog/Product/Service/Batch.php @@ -25,7 +25,7 @@ { public function __construct( protected BatchOperationsInterface $batch, - protected LoggerInterface $log) - { + protected LoggerInterface $log + ) { } -} \ No newline at end of file +} diff --git a/src/Services/Catalog/Product/Service/Product.php b/src/Services/Catalog/Product/Service/Product.php index 730fd7c9..d37d34f6 100644 --- a/src/Services/Catalog/Product/Service/Product.php +++ b/src/Services/Catalog/Product/Service/Product.php @@ -25,7 +25,6 @@ use Bitrix24\SDK\Services\Catalog\Common\ProductType; use Bitrix24\SDK\Services\Catalog\Product\Result\ProductResult; use Bitrix24\SDK\Services\Catalog\Product\Result\ProductsResult; - use Psr\Log\LoggerInterface; #[ApiServiceMetadata(new Scope(['catalog']))] @@ -35,8 +34,7 @@ public function __construct( public Batch $batch, CoreInterface $core, LoggerInterface $logger - ) - { + ) { parent::__construct($core, $logger); } @@ -71,7 +69,9 @@ public function get(int $productId): ProductResult )] public function add(array $productFields): ProductResult { - return new ProductResult($this->core->call('catalog.product.add', [ + return new ProductResult($this->core->call( + 'catalog.product.add', + [ 'fields' => $productFields ] )); @@ -140,4 +140,4 @@ public function fieldsByFilter(int $iblockId, ProductType $productType, ?array $ return new FieldsResult($this->core->call('catalog.product.getFieldsByFilter', ['filter' => $filter])); } -} \ No newline at end of file +} diff --git a/tests/Integration/Services/Catalog/Document/Result/DocumentItemResultTest.php b/tests/Integration/Services/Catalog/Document/Result/DocumentItemResultTest.php new file mode 100644 index 00000000..66ca98f2 --- /dev/null +++ b/tests/Integration/Services/Catalog/Document/Result/DocumentItemResultTest.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\Tests\Integration\Services\Catalog\Document\Result; + +use Bitrix24\SDK\Services\Catalog\Document\Result\DocumentItemResult; +use Bitrix24\SDK\Services\Catalog\Document\Service\Document; +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(DocumentItemResult::class)] +class DocumentItemResultTest extends TestCase +{ + use CustomBitrix24Assertions; + + private Document $documentService; + + private int $documentId; + + #[\Override] + protected function setUp(): void + { + $this->documentService = Fabric::getServiceBuilder(true)->getCatalogScope()->document(); + $this->documentId = $this->documentService->add([ + 'docType' => 'A', + 'currency' => 'USD', + 'responsibleId' => 1, + 'title' => sprintf('test document annotations %s', time()), + ])->document()->id; + } + + #[\Override] + protected function tearDown(): void + { + $this->documentService->delete($this->documentId); + } + + #[Test] + #[TestDox('all fields in DocumentItemResult are annotated in phpdoc and match with raw api response')] + public function testAllFieldsAreAnnotated(): void + { + $rawItem = $this->documentService->list([], ['id' => $this->documentId]) + ->getCoreResponse()->getResponseData()->getResult()['documents'][0]; + $this->assertBitrix24AllResultItemFieldsAnnotated(array_keys($rawItem), DocumentItemResult::class); + } + + #[Test] + #[TestDox('all fields in DocumentItemResult have valid type casting in magic getters')] + public function testAllFieldsHasValidTypeCastingInMagicGetters(): void + { + $documentItemResult = $this->documentService->list([], ['id' => $this->documentId])->getDocuments()[0]; + $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations($documentItemResult, DocumentItemResult::class); + } +} diff --git a/tests/Integration/Services/Catalog/Document/Service/BatchTest.php b/tests/Integration/Services/Catalog/Document/Service/BatchTest.php new file mode 100644 index 00000000..8c1f7807 --- /dev/null +++ b/tests/Integration/Services/Catalog/Document/Service/BatchTest.php @@ -0,0 +1,73 @@ + + * + * 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\Document\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Catalog\Document\Service\Batch; +use Bitrix24\SDK\Services\Catalog\Document\Service\Document; +use Bitrix24\SDK\Tests\Integration\Fabric; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Batch::class)] +class BatchTest extends TestCase +{ + private Document $documentService; + + #[\Override] + protected function setUp(): void + { + $this->documentService = Fabric::getServiceBuilder(true)->getCatalogScope()->document(); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[TestDox('test Batch::add, Batch::update, Batch::delete')] + public function testAddUpdateDelete(): void + { + $addedIds = []; + foreach ($this->documentService->batch->add([ + ['docType' => 'A', 'currency' => 'USD', 'responsibleId' => 1, 'title' => sprintf('batch document %s', time())], + ]) as $addedItemResult) { + $addedIds[] = $addedItemResult->document()->id; + } + + $this->assertCount(1, $addedIds); + + $updatePayload = []; + foreach ($addedIds as $id) { + $updatePayload[$id] = ['title' => 'updated batch document']; + } + + $updatedCount = 0; + foreach ($this->documentService->batch->update($updatePayload) as $updatedItemResult) { + $this->assertSame('updated batch document', $updatedItemResult->document()->title); + $updatedCount++; + } + + $this->assertSame(1, $updatedCount); + + $deletedCount = 0; + foreach ($this->documentService->batch->delete($addedIds) as $deletedItemResult) { + $this->assertTrue($deletedItemResult->isSuccess()); + $deletedCount++; + } + + $this->assertSame(1, $deletedCount); + } +} diff --git a/tests/Integration/Services/Catalog/Document/Service/DocumentTest.php b/tests/Integration/Services/Catalog/Document/Service/DocumentTest.php new file mode 100644 index 00000000..75c6a731 --- /dev/null +++ b/tests/Integration/Services/Catalog/Document/Service/DocumentTest.php @@ -0,0 +1,222 @@ + + * + * 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\Document\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Catalog\Document\Service\Document; +use Bitrix24\SDK\Services\Catalog\DocumentElement\Service\DocumentElement; +use Bitrix24\SDK\Services\Catalog\Product\Service\Product; +use Bitrix24\SDK\Tests\Integration\Fabric; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Document::class)] +class DocumentTest extends TestCase +{ + private Document $documentService; + + private DocumentElement $documentElementService; + + private Product $productService; + + /** + * @var int[] + */ + private array $createdDocumentIds = []; + + /** + * @var int[] + */ + private array $createdProductIds = []; + + #[\Override] + protected function setUp(): void + { + $serviceBuilder = Fabric::getServiceBuilder(true); + $this->documentService = $serviceBuilder->getCatalogScope()->document(); + $this->documentElementService = $serviceBuilder->getCatalogScope()->documentElement(); + $this->productService = $serviceBuilder->getCatalogScope()->product(); + } + + #[\Override] + protected function tearDown(): void + { + foreach ($this->createdDocumentIds as $documentId) { + try { + $this->documentService->cancel($documentId); + } catch (\Throwable) { + // document may not be conducted, ignore + } + + try { + $this->documentService->delete($documentId); + } catch (\Throwable) { + // already removed, ignore + } + } + + foreach ($this->createdProductIds as $productId) { + try { + $this->productService->delete($productId); + } catch (\Throwable) { + // already removed, ignore + } + } + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[TestDox('test Document::add, Document::update, Document::list, Document::delete')] + public function testAddUpdateListDelete(): void + { + $title = sprintf('test document %s', time()); + $addResult = $this->documentService->add([ + 'docType' => 'A', + 'currency' => 'USD', + 'responsibleId' => 1, + 'title' => $title, + ]); + $documentId = $addResult->document()->id; + $this->createdDocumentIds[] = $documentId; + + $this->assertSame($title, $addResult->document()->title); + $this->assertSame('A', $addResult->document()->docType); + + $updatedTitle = sprintf('updated test document %s', time()); + $updateResult = $this->documentService->update($documentId, ['title' => $updatedTitle]); + $this->assertSame($updatedTitle, $updateResult->document()->title); + + $listResult = $this->documentService->list([], ['id' => $documentId]); + $this->assertCount(1, $listResult->getDocuments()); + + $this->assertTrue($this->documentService->delete($documentId)->isSuccess()); + $this->createdDocumentIds = array_diff($this->createdDocumentIds, [$documentId]); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[TestDox('test Document::deleteList')] + public function testDeleteList(): void + { + $documentIds = []; + for ($i = 0; $i < 2; ++$i) { + $documentIds[] = $this->documentService->add([ + 'docType' => 'A', + 'currency' => 'USD', + 'responsibleId' => 1, + 'title' => sprintf('test document deleteList %s-%s', time(), $i), + ])->document()->id; + } + + $this->createdDocumentIds = array_merge($this->createdDocumentIds, $documentIds); + + $this->assertTrue($this->documentService->deleteList($documentIds)->isSuccess()); + $this->createdDocumentIds = array_values(array_diff($this->createdDocumentIds, $documentIds)); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[TestDox('test Document::getFields')] + public function testGetFields(): void + { + $this->assertIsArray($this->documentService->getFields()->getFieldsDescription()); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[TestDox('test Document::modeStatus')] + public function testModeStatus(): void + { + $this->assertIsBool($this->documentService->modeStatus()->isEnabled()); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[TestDox('test Document::conduct, Document::cancel')] + public function testConductCancel(): void + { + $documentId = $this->createDocumentWithElement('test document conduct'); + + $this->assertTrue($this->documentService->conduct($documentId)->isSuccess()); + $this->assertTrue($this->documentService->cancel($documentId)->isSuccess()); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[TestDox('test Document::conductList, Document::cancelList')] + public function testConductListCancelList(): void + { + $documentIds = [ + $this->createDocumentWithElement('test document conductList 0'), + $this->createDocumentWithElement('test document conductList 1'), + ]; + + $this->assertTrue($this->documentService->conductList($documentIds)->isSuccess()); + $this->assertTrue($this->documentService->cancelList($documentIds)->isSuccess()); + } + + /** + * Creates a stock-taking document ('S') with one line item so it can be conducted. + * docType 'S' is used because it does not require a supplier, unlike docType 'A' (goods receipt). + * + * @throws BaseException + * @throws TransportException + */ + private function createDocumentWithElement(string $title): int + { + $iblockId = Fabric::getServiceBuilder(true)->getCatalogScope()->catalog() + ->list([], [], [], 1)->getCatalogs()[0]->iblockId; + + $stores = Fabric::getCore(true)->call('catalog.store.list', ['select' => ['id'], 'filter' => ['active' => 'Y']]) + ->getResponseData()->getResult(); + $storeId = $stores['stores'][0]['id']; + + $productId = $this->productService->add([ + 'iblockId' => $iblockId, + 'name' => sprintf('%s product %s', $title, time()), + ])->product()->id; + $this->createdProductIds[] = $productId; + + $documentId = $this->documentService->add([ + 'docType' => 'S', + 'currency' => 'USD', + 'responsibleId' => 1, + 'title' => sprintf('%s %s', $title, time()), + ])->document()->id; + $this->createdDocumentIds[] = $documentId; + + $this->documentElementService->add([ + 'docId' => $documentId, + 'elementId' => $productId, + 'storeTo' => $storeId, + 'amount' => 1, + ]); + + return $documentId; + } +} diff --git a/tests/Integration/Services/Catalog/DocumentElement/Result/DocumentElementItemResultTest.php b/tests/Integration/Services/Catalog/DocumentElement/Result/DocumentElementItemResultTest.php new file mode 100644 index 00000000..4f967099 --- /dev/null +++ b/tests/Integration/Services/Catalog/DocumentElement/Result/DocumentElementItemResultTest.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\Catalog\DocumentElement\Result; + +use Bitrix24\SDK\Services\Catalog\Document\Service\Document; +use Bitrix24\SDK\Services\Catalog\DocumentElement\Result\DocumentElementItemResult; +use Bitrix24\SDK\Services\Catalog\DocumentElement\Service\DocumentElement; +use Bitrix24\SDK\Services\Catalog\Product\Service\Product; +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(DocumentElementItemResult::class)] +class DocumentElementItemResultTest extends TestCase +{ + use CustomBitrix24Assertions; + + private DocumentElement $documentElementService; + + private Document $documentService; + + private Product $productService; + + private int $documentId; + + private int $productId; + + #[\Override] + protected function setUp(): void + { + $serviceBuilder = Fabric::getServiceBuilder(); + $this->documentElementService = $serviceBuilder->getCatalogScope()->documentElement(); + $this->documentService = $serviceBuilder->getCatalogScope()->document(); + $this->productService = $serviceBuilder->getCatalogScope()->product(); + + $catalogService = $serviceBuilder->getCatalogScope()->catalog(); + $iblockId = $catalogService->list([], [], [], 1)->getCatalogs()[0]->iblockId; + + $this->productId = $this->productService->add([ + 'iblockId' => $iblockId, + 'name' => sprintf('test document element annotations product %s', time()), + ])->product()->id; + + $this->documentId = $this->documentService->add([ + 'docType' => 'A', + 'currency' => 'USD', + 'responsibleId' => 1, + 'title' => sprintf('test document element annotations %s', time()), + ])->document()->id; + + $this->documentElementService->add([ + 'docId' => $this->documentId, + 'elementId' => $this->productId, + 'storeTo' => 0, + 'amount' => 2, + ]); + } + + #[\Override] + protected function tearDown(): void + { + try { + $this->documentService->delete($this->documentId); + } catch (\Throwable) { + // already removed, ignore + } + + try { + $this->productService->delete($this->productId); + } catch (\Throwable) { + // already removed, ignore + } + } + + #[Test] + #[TestDox('all fields in DocumentElementItemResult are annotated in phpdoc and match with raw api response')] + public function testAllFieldsAreAnnotated(): void + { + $rawItem = $this->documentElementService->list([], ['docId' => $this->documentId]) + ->getCoreResponse()->getResponseData()->getResult()['documentElements'][0]; + $this->assertBitrix24AllResultItemFieldsAnnotated(array_keys($rawItem), DocumentElementItemResult::class); + } + + #[Test] + #[TestDox('all fields in DocumentElementItemResult have valid type casting in magic getters')] + public function testAllFieldsHasValidTypeCastingInMagicGetters(): void + { + $documentElementItemResult = $this->documentElementService->list([], ['docId' => $this->documentId])->getDocumentElements()[0]; + $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations($documentElementItemResult, DocumentElementItemResult::class); + } +} diff --git a/tests/Integration/Services/Catalog/DocumentElement/Service/BatchTest.php b/tests/Integration/Services/Catalog/DocumentElement/Service/BatchTest.php new file mode 100644 index 00000000..3a2a75f6 --- /dev/null +++ b/tests/Integration/Services/Catalog/DocumentElement/Service/BatchTest.php @@ -0,0 +1,121 @@ + + * + * 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\DocumentElement\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Catalog\Document\Service\Document; +use Bitrix24\SDK\Services\Catalog\DocumentElement\Service\Batch; +use Bitrix24\SDK\Services\Catalog\DocumentElement\Service\DocumentElement; +use Bitrix24\SDK\Services\Catalog\Product\Service\Product; +use Bitrix24\SDK\Tests\Integration\Fabric; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(Batch::class)] +class BatchTest extends TestCase +{ + private DocumentElement $documentElementService; + + private Document $documentService; + + private Product $productService; + + private int $documentId; + + private int $productId; + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function setUp(): void + { + $serviceBuilder = Fabric::getServiceBuilder(); + $this->documentElementService = $serviceBuilder->getCatalogScope()->documentElement(); + $this->documentService = $serviceBuilder->getCatalogScope()->document(); + $this->productService = $serviceBuilder->getCatalogScope()->product(); + + $catalogService = $serviceBuilder->getCatalogScope()->catalog(); + $iblockId = $catalogService->list([], [], [], 1)->getCatalogs()[0]->iblockId; + + $this->productId = $this->productService->add([ + 'iblockId' => $iblockId, + 'name' => sprintf('test batch document element product %s', time()), + ])->product()->id; + + $this->documentId = $this->documentService->add([ + 'docType' => 'A', + 'currency' => 'USD', + 'responsibleId' => 1, + 'title' => sprintf('test batch document for element %s', time()), + ])->document()->id; + } + + #[\Override] + protected function tearDown(): void + { + try { + $this->documentService->delete($this->documentId); + } catch (\Throwable) { + // already removed, ignore + } + + try { + $this->productService->delete($this->productId); + } catch (\Throwable) { + // already removed, ignore + } + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[TestDox('test Batch::add, Batch::update, Batch::delete')] + public function testAddUpdateDelete(): void + { + $addedIds = []; + foreach ($this->documentElementService->batch->add([ + ['docId' => $this->documentId, 'elementId' => $this->productId, 'storeTo' => 0, 'amount' => 3], + ]) as $addedItemResult) { + $addedIds[] = $addedItemResult->documentElement()->id; + } + + $this->assertCount(1, $addedIds); + + $updatePayload = []; + foreach ($addedIds as $id) { + $updatePayload[$id] = ['amount' => 6]; + } + + $updatedCount = 0; + foreach ($this->documentElementService->batch->update($updatePayload) as $updatedItemResult) { + $this->assertEqualsWithDelta(6.0, $updatedItemResult->documentElement()->amount, PHP_FLOAT_EPSILON); + $updatedCount++; + } + + $this->assertSame(1, $updatedCount); + + $deletedCount = 0; + foreach ($this->documentElementService->batch->delete($addedIds) as $deletedItemResult) { + $this->assertTrue($deletedItemResult->isSuccess()); + $deletedCount++; + } + + $this->assertSame(1, $deletedCount); + } +} diff --git a/tests/Integration/Services/Catalog/DocumentElement/Service/DocumentElementTest.php b/tests/Integration/Services/Catalog/DocumentElement/Service/DocumentElementTest.php new file mode 100644 index 00000000..162c33e8 --- /dev/null +++ b/tests/Integration/Services/Catalog/DocumentElement/Service/DocumentElementTest.php @@ -0,0 +1,120 @@ + + * + * 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\DocumentElement\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Catalog\Document\Service\Document; +use Bitrix24\SDK\Services\Catalog\DocumentElement\Service\DocumentElement; +use Bitrix24\SDK\Services\Catalog\Product\Service\Product; +use Bitrix24\SDK\Tests\Integration\Fabric; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(DocumentElement::class)] +class DocumentElementTest extends TestCase +{ + private DocumentElement $documentElementService; + + private Document $documentService; + + private Product $productService; + + private int $documentId; + + private int $productId; + + /** + * @throws BaseException + * @throws TransportException + */ + #[\Override] + protected function setUp(): void + { + $serviceBuilder = Fabric::getServiceBuilder(); + $this->documentElementService = $serviceBuilder->getCatalogScope()->documentElement(); + $this->documentService = $serviceBuilder->getCatalogScope()->document(); + $this->productService = $serviceBuilder->getCatalogScope()->product(); + + $catalogService = $serviceBuilder->getCatalogScope()->catalog(); + $iblockId = $catalogService->list([], [], [], 1)->getCatalogs()[0]->iblockId; + + $this->productId = $this->productService->add([ + 'iblockId' => $iblockId, + 'name' => sprintf('test document element product %s', time()), + ])->product()->id; + + $this->documentId = $this->documentService->add([ + 'docType' => 'A', + 'currency' => 'USD', + 'responsibleId' => 1, + 'title' => sprintf('test document for element %s', time()), + ])->document()->id; + } + + #[\Override] + protected function tearDown(): void + { + try { + $this->documentService->delete($this->documentId); + } catch (\Throwable) { + // already removed, ignore + } + + try { + $this->productService->delete($this->productId); + } catch (\Throwable) { + // already removed, ignore + } + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[TestDox('test DocumentElement::add, DocumentElement::update, DocumentElement::list, DocumentElement::delete')] + public function testAddUpdateListDelete(): void + { + $addResult = $this->documentElementService->add([ + 'docId' => $this->documentId, + 'elementId' => $this->productId, + 'storeTo' => 0, + 'amount' => 5, + 'purchasingPrice' => 10.5, + ]); + $documentElementId = $addResult->documentElement()->id; + + $this->assertSame($this->documentId, $addResult->documentElement()->docId); + $this->assertSame($this->productId, $addResult->documentElement()->elementId); + + $updateResult = $this->documentElementService->update($documentElementId, ['amount' => 8]); + $this->assertEqualsWithDelta(8.0, $updateResult->documentElement()->amount, PHP_FLOAT_EPSILON); + + $listResult = $this->documentElementService->list([], ['docId' => $this->documentId]); + $this->assertCount(1, $listResult->getDocumentElements()); + + $this->assertTrue($this->documentElementService->delete($documentElementId)->isSuccess()); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[TestDox('test DocumentElement::getFields')] + public function testGetFields(): void + { + $this->assertIsArray($this->documentElementService->getFields()->getFieldsDescription()); + } +} diff --git a/tests/Unit/Services/Catalog/Document/Service/DocumentTest.php b/tests/Unit/Services/Catalog/Document/Service/DocumentTest.php new file mode 100644 index 00000000..8f2d2bdf --- /dev/null +++ b/tests/Unit/Services/Catalog/Document/Service/DocumentTest.php @@ -0,0 +1,134 @@ +mockCore('catalog.document.add', [ + 'fields' => ['docType' => 'A', 'currency' => 'RUB', 'responsibleId' => 29], + ]); + + self::assertInstanceOf( + DocumentResult::class, + $this->makeService($core)->add(['docType' => 'A', 'currency' => 'RUB', 'responsibleId' => 29]) + ); + } + + public function testUpdateBuildsParameters(): void + { + $core = $this->mockCore('catalog.document.update', [ + 'id' => 142, + 'fields' => ['title' => 'Updated title'], + ]); + + self::assertInstanceOf( + DocumentResult::class, + $this->makeService($core)->update(142, ['title' => 'Updated title']) + ); + } + + public function testListBuildsParameters(): void + { + $core = $this->mockCore('catalog.document.list', [ + 'select' => ['id', 'docType'], + 'filter' => ['docType' => 'A'], + ]); + + self::assertInstanceOf( + DocumentsResult::class, + $this->makeService($core)->list(['id', 'docType'], ['docType' => 'A']) + ); + } + + public function testDeleteBuildsParameters(): void + { + $core = $this->mockCore('catalog.document.delete', ['id' => 142]); + + self::assertInstanceOf(DeletedItemResult::class, $this->makeService($core)->delete(142)); + } + + public function testDeleteListBuildsParameters(): void + { + $core = $this->mockCore('catalog.document.deleteList', ['documentIds' => [142, 143]]); + + self::assertInstanceOf(DeletedItemResult::class, $this->makeService($core)->deleteList([142, 143])); + } + + public function testConductBuildsParameters(): void + { + $core = $this->mockCore('catalog.document.conduct', ['id' => 142]); + + self::assertInstanceOf(DeletedItemResult::class, $this->makeService($core)->conduct(142)); + } + + public function testConductListBuildsParameters(): void + { + $core = $this->mockCore('catalog.document.conductList', ['documentIds' => [142, 143]]); + + self::assertInstanceOf(DeletedItemResult::class, $this->makeService($core)->conductList([142, 143])); + } + + public function testCancelBuildsParameters(): void + { + $core = $this->mockCore('catalog.document.cancel', ['id' => 142]); + + self::assertInstanceOf(DeletedItemResult::class, $this->makeService($core)->cancel(142)); + } + + public function testCancelListBuildsParameters(): void + { + $core = $this->mockCore('catalog.document.cancelList', ['documentIds' => [142, 143]]); + + self::assertInstanceOf(DeletedItemResult::class, $this->makeService($core)->cancelList([142, 143])); + } + + public function testGetFieldsBuildsParameters(): void + { + $core = $this->mockCore('catalog.document.getFields', []); + + self::assertInstanceOf(DocumentFieldsResult::class, $this->makeService($core)->getFields()); + } + + public function testModeStatusBuildsParameters(): void + { + $core = $this->mockCore('catalog.document.mode.status', []); + + self::assertInstanceOf(DocumentModeStatusResult::class, $this->makeService($core)->modeStatus()); + } + + private function makeService(CoreInterface $core): Document + { + return new Document(new Batch(new DocumentBatch($core, new NullLogger()), new NullLogger()), $core, new NullLogger()); + } + + private function mockCore(string $method, array $parameters): CoreInterface + { + $response = $this->createStub(Response::class); + $core = $this->createMock(CoreInterface::class); + $core->expects($this->once()) + ->method('call') + ->with($method, $parameters) + ->willReturn($response); + + return $core; + } +} diff --git a/tests/Unit/Services/Catalog/DocumentElement/Service/DocumentElementTest.php b/tests/Unit/Services/Catalog/DocumentElement/Service/DocumentElementTest.php new file mode 100644 index 00000000..ff3b48d5 --- /dev/null +++ b/tests/Unit/Services/Catalog/DocumentElement/Service/DocumentElementTest.php @@ -0,0 +1,96 @@ +mockCore('catalog.document.element.add', [ + 'fields' => ['docId' => 64, 'elementId' => 312, 'storeTo' => 2, 'amount' => 15, 'purchasingPrice' => 1250.5], + ]); + + self::assertInstanceOf( + DocumentElementResult::class, + $this->makeService($core)->add(['docId' => 64, 'elementId' => 312, 'storeTo' => 2, 'amount' => 15, 'purchasingPrice' => 1250.5]) + ); + } + + public function testUpdateBuildsParameters(): void + { + $core = $this->mockCore('catalog.document.element.update', [ + 'id' => 148, + 'fields' => ['amount' => 12, 'purchasingPrice' => 1180, 'storeTo' => 2], + ]); + + self::assertInstanceOf( + DocumentElementResult::class, + $this->makeService($core)->update(148, ['amount' => 12, 'purchasingPrice' => 1180, 'storeTo' => 2]) + ); + } + + public function testListBuildsParameters(): void + { + $core = $this->mockCore('catalog.document.element.list', [ + 'select' => ['id', 'docId', 'elementId'], + 'filter' => ['docId' => 64], + 'order' => ['id' => 'ASC'], + ]); + + self::assertInstanceOf( + DocumentElementsResult::class, + $this->makeService($core)->list(['id', 'docId', 'elementId'], ['docId' => 64], ['id' => 'ASC']) + ); + } + + public function testDeleteBuildsParameters(): void + { + $core = $this->mockCore('catalog.document.element.delete', ['id' => 148]); + + self::assertInstanceOf(DeletedItemResult::class, $this->makeService($core)->delete(148)); + } + + public function testGetFieldsBuildsParameters(): void + { + $core = $this->mockCore('catalog.document.element.getFields', []); + + self::assertInstanceOf(DocumentElementFieldsResult::class, $this->makeService($core)->getFields()); + } + + private function makeService(CoreInterface $core): DocumentElement + { + return new DocumentElement( + new Batch(new DocumentElementBatch($core, new NullLogger()), new NullLogger()), + $core, + new NullLogger() + ); + } + + private function mockCore(string $method, array $parameters): CoreInterface + { + $response = $this->createStub(Response::class); + $core = $this->createMock(CoreInterface::class); + $core->expects($this->once()) + ->method('call') + ->with($method, $parameters) + ->willReturn($response); + + return $core; + } +} From 0cff7ad9bdee628c06a4578557d9eae4e115ea83 Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Tue, 4 Aug 2026 20:03:21 +0400 Subject: [PATCH 2/3] Fix on liter results --- .../Document/Result/DocumentItemResult.php | 4 ++-- .../Result/DocumentElementItemResult.php | 4 ++-- .../Result/DocumentItemResultTest.php | 6 +++-- .../Catalog/Document/Service/BatchTest.php | 4 ++-- .../Catalog/Document/Service/DocumentTest.php | 22 +++++++++---------- .../Result/DocumentElementItemResultTest.php | 6 +++-- .../DocumentElement/Service/BatchTest.php | 4 ++-- .../Service/DocumentElementTest.php | 12 +++++----- 8 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/Services/Catalog/Document/Result/DocumentItemResult.php b/src/Services/Catalog/Document/Result/DocumentItemResult.php index 742e4be1..4693e40b 100644 --- a/src/Services/Catalog/Document/Result/DocumentItemResult.php +++ b/src/Services/Catalog/Document/Result/DocumentItemResult.php @@ -13,7 +13,7 @@ namespace Bitrix24\SDK\Services\Catalog\Document\Result; -use Bitrix24\SDK\Core\Result\AbstractAnnotatedItem; +use Bitrix24\SDK\Core\Result\AbstractItem; use Carbon\CarbonImmutable; /** @@ -35,6 +35,6 @@ * @property-read string|null $status * @property-read int|null $statusBy */ -class DocumentItemResult extends AbstractAnnotatedItem +class DocumentItemResult extends AbstractItem { } diff --git a/src/Services/Catalog/DocumentElement/Result/DocumentElementItemResult.php b/src/Services/Catalog/DocumentElement/Result/DocumentElementItemResult.php index 3c6df0f2..333feca2 100644 --- a/src/Services/Catalog/DocumentElement/Result/DocumentElementItemResult.php +++ b/src/Services/Catalog/DocumentElement/Result/DocumentElementItemResult.php @@ -13,7 +13,7 @@ namespace Bitrix24\SDK\Services\Catalog\DocumentElement\Result; -use Bitrix24\SDK\Core\Result\AbstractAnnotatedItem; +use Bitrix24\SDK\Core\Result\AbstractItem; /** * @property-read int $id @@ -24,6 +24,6 @@ * @property-read float $amount * @property-read float|null $purchasingPrice */ -class DocumentElementItemResult extends AbstractAnnotatedItem +class DocumentElementItemResult extends AbstractItem { } diff --git a/tests/Integration/Services/Catalog/Document/Result/DocumentItemResultTest.php b/tests/Integration/Services/Catalog/Document/Result/DocumentItemResultTest.php index 66ca98f2..f3117829 100644 --- a/tests/Integration/Services/Catalog/Document/Result/DocumentItemResultTest.php +++ b/tests/Integration/Services/Catalog/Document/Result/DocumentItemResultTest.php @@ -62,7 +62,9 @@ public function testAllFieldsAreAnnotated(): void #[TestDox('all fields in DocumentItemResult have valid type casting in magic getters')] public function testAllFieldsHasValidTypeCastingInMagicGetters(): void { - $documentItemResult = $this->documentService->list([], ['id' => $this->documentId])->getDocuments()[0]; - $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations($documentItemResult, DocumentItemResult::class); + $this->assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( + $this->documentService->getFields()->getFieldsDescription(), + DocumentItemResult::class + ); } } diff --git a/tests/Integration/Services/Catalog/Document/Service/BatchTest.php b/tests/Integration/Services/Catalog/Document/Service/BatchTest.php index 8c1f7807..655e8084 100644 --- a/tests/Integration/Services/Catalog/Document/Service/BatchTest.php +++ b/tests/Integration/Services/Catalog/Document/Service/BatchTest.php @@ -50,8 +50,8 @@ public function testAddUpdateDelete(): void $this->assertCount(1, $addedIds); $updatePayload = []; - foreach ($addedIds as $id) { - $updatePayload[$id] = ['title' => 'updated batch document']; + foreach ($addedIds as $addedId) { + $updatePayload[$addedId] = ['title' => 'updated batch document']; } $updatedCount = 0; diff --git a/tests/Integration/Services/Catalog/Document/Service/DocumentTest.php b/tests/Integration/Services/Catalog/Document/Service/DocumentTest.php index 75c6a731..271ff3db 100644 --- a/tests/Integration/Services/Catalog/Document/Service/DocumentTest.php +++ b/tests/Integration/Services/Catalog/Document/Service/DocumentTest.php @@ -54,23 +54,23 @@ protected function setUp(): void #[\Override] protected function tearDown(): void { - foreach ($this->createdDocumentIds as $documentId) { + foreach ($this->createdDocumentIds as $createdDocumentId) { try { - $this->documentService->cancel($documentId); + $this->documentService->cancel($createdDocumentId); } catch (\Throwable) { // document may not be conducted, ignore } try { - $this->documentService->delete($documentId); + $this->documentService->delete($createdDocumentId); } catch (\Throwable) { // already removed, ignore } } - foreach ($this->createdProductIds as $productId) { + foreach ($this->createdProductIds as $createdProductId) { try { - $this->productService->delete($productId); + $this->productService->delete($createdProductId); } catch (\Throwable) { // already removed, ignore } @@ -85,24 +85,24 @@ protected function tearDown(): void public function testAddUpdateListDelete(): void { $title = sprintf('test document %s', time()); - $addResult = $this->documentService->add([ + $documentResult = $this->documentService->add([ 'docType' => 'A', 'currency' => 'USD', 'responsibleId' => 1, 'title' => $title, ]); - $documentId = $addResult->document()->id; + $documentId = $documentResult->document()->id; $this->createdDocumentIds[] = $documentId; - $this->assertSame($title, $addResult->document()->title); - $this->assertSame('A', $addResult->document()->docType); + $this->assertSame($title, $documentResult->document()->title); + $this->assertSame('A', $documentResult->document()->docType); $updatedTitle = sprintf('updated test document %s', time()); $updateResult = $this->documentService->update($documentId, ['title' => $updatedTitle]); $this->assertSame($updatedTitle, $updateResult->document()->title); - $listResult = $this->documentService->list([], ['id' => $documentId]); - $this->assertCount(1, $listResult->getDocuments()); + $documentsResult = $this->documentService->list([], ['id' => $documentId]); + $this->assertCount(1, $documentsResult->getDocuments()); $this->assertTrue($this->documentService->delete($documentId)->isSuccess()); $this->createdDocumentIds = array_diff($this->createdDocumentIds, [$documentId]); diff --git a/tests/Integration/Services/Catalog/DocumentElement/Result/DocumentElementItemResultTest.php b/tests/Integration/Services/Catalog/DocumentElement/Result/DocumentElementItemResultTest.php index 4f967099..8b1364bc 100644 --- a/tests/Integration/Services/Catalog/DocumentElement/Result/DocumentElementItemResultTest.php +++ b/tests/Integration/Services/Catalog/DocumentElement/Result/DocumentElementItemResultTest.php @@ -99,7 +99,9 @@ public function testAllFieldsAreAnnotated(): void #[TestDox('all fields in DocumentElementItemResult have valid type casting in magic getters')] public function testAllFieldsHasValidTypeCastingInMagicGetters(): void { - $documentElementItemResult = $this->documentElementService->list([], ['docId' => $this->documentId])->getDocumentElements()[0]; - $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations($documentElementItemResult, DocumentElementItemResult::class); + $this->assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( + $this->documentElementService->getFields()->getFieldsDescription(), + DocumentElementItemResult::class + ); } } diff --git a/tests/Integration/Services/Catalog/DocumentElement/Service/BatchTest.php b/tests/Integration/Services/Catalog/DocumentElement/Service/BatchTest.php index 3a2a75f6..f831d659 100644 --- a/tests/Integration/Services/Catalog/DocumentElement/Service/BatchTest.php +++ b/tests/Integration/Services/Catalog/DocumentElement/Service/BatchTest.php @@ -98,8 +98,8 @@ public function testAddUpdateDelete(): void $this->assertCount(1, $addedIds); $updatePayload = []; - foreach ($addedIds as $id) { - $updatePayload[$id] = ['amount' => 6]; + foreach ($addedIds as $addedId) { + $updatePayload[$addedId] = ['amount' => 6]; } $updatedCount = 0; diff --git a/tests/Integration/Services/Catalog/DocumentElement/Service/DocumentElementTest.php b/tests/Integration/Services/Catalog/DocumentElement/Service/DocumentElementTest.php index 162c33e8..b04e15a9 100644 --- a/tests/Integration/Services/Catalog/DocumentElement/Service/DocumentElementTest.php +++ b/tests/Integration/Services/Catalog/DocumentElement/Service/DocumentElementTest.php @@ -87,23 +87,23 @@ protected function tearDown(): void #[TestDox('test DocumentElement::add, DocumentElement::update, DocumentElement::list, DocumentElement::delete')] public function testAddUpdateListDelete(): void { - $addResult = $this->documentElementService->add([ + $documentElementResult = $this->documentElementService->add([ 'docId' => $this->documentId, 'elementId' => $this->productId, 'storeTo' => 0, 'amount' => 5, 'purchasingPrice' => 10.5, ]); - $documentElementId = $addResult->documentElement()->id; + $documentElementId = $documentElementResult->documentElement()->id; - $this->assertSame($this->documentId, $addResult->documentElement()->docId); - $this->assertSame($this->productId, $addResult->documentElement()->elementId); + $this->assertSame($this->documentId, $documentElementResult->documentElement()->docId); + $this->assertSame($this->productId, $documentElementResult->documentElement()->elementId); $updateResult = $this->documentElementService->update($documentElementId, ['amount' => 8]); $this->assertEqualsWithDelta(8.0, $updateResult->documentElement()->amount, PHP_FLOAT_EPSILON); - $listResult = $this->documentElementService->list([], ['docId' => $this->documentId]); - $this->assertCount(1, $listResult->getDocumentElements()); + $documentElementsResult = $this->documentElementService->list([], ['docId' => $this->documentId]); + $this->assertCount(1, $documentElementsResult->getDocumentElements()); $this->assertTrue($this->documentElementService->delete($documentElementId)->isSuccess()); } From 18704614d216a3ad2a081231031e1928f16f08f7 Mon Sep 17 00:00:00 2001 From: Dmitriy Ignatenko Date: Tue, 4 Aug 2026 20:25:39 +0400 Subject: [PATCH 3/3] Fix on test results --- .../Document/Result/DocumentItemResult.php | 3 +- .../Result/DocumentElementItemResult.php | 3 +- .../CustomBitrix24Assertions.php | 31 +++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/Services/Catalog/Document/Result/DocumentItemResult.php b/src/Services/Catalog/Document/Result/DocumentItemResult.php index 4693e40b..bc059691 100644 --- a/src/Services/Catalog/Document/Result/DocumentItemResult.php +++ b/src/Services/Catalog/Document/Result/DocumentItemResult.php @@ -15,6 +15,7 @@ use Bitrix24\SDK\Core\Result\AbstractItem; use Carbon\CarbonImmutable; +use Money\Money; /** * @property-read int $id @@ -28,7 +29,7 @@ * @property-read CarbonImmutable|null $dateStatus * @property-read string|null $title * @property-read string|null $commentary - * @property-read float|null $total + * @property-read Money|null $total * @property-read string|null $docNumber * @property-read int|null $createdBy * @property-read int|null $modifiedBy diff --git a/src/Services/Catalog/DocumentElement/Result/DocumentElementItemResult.php b/src/Services/Catalog/DocumentElement/Result/DocumentElementItemResult.php index 333feca2..b82733de 100644 --- a/src/Services/Catalog/DocumentElement/Result/DocumentElementItemResult.php +++ b/src/Services/Catalog/DocumentElement/Result/DocumentElementItemResult.php @@ -14,6 +14,7 @@ namespace Bitrix24\SDK\Services\Catalog\DocumentElement\Result; use Bitrix24\SDK\Core\Result\AbstractItem; +use Money\Money; /** * @property-read int $id @@ -22,7 +23,7 @@ * @property-read int|null $storeFrom * @property-read int|null $storeTo * @property-read float $amount - * @property-read float|null $purchasingPrice + * @property-read Money|null $purchasingPrice */ class DocumentElementItemResult extends AbstractItem { diff --git a/tests/CustomAssertions/CustomBitrix24Assertions.php b/tests/CustomAssertions/CustomBitrix24Assertions.php index dd1d32b7..af8be28d 100644 --- a/tests/CustomAssertions/CustomBitrix24Assertions.php +++ b/tests/CustomAssertions/CustomBitrix24Assertions.php @@ -225,6 +225,21 @@ protected function assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( ); break; } + // catalog.document.element field «amount» is a stock quantity, not a monetary amount + if ($fieldCode === 'amount') { + $this->assertTrue( + str_contains($propsFromAnnotations[$fieldCode], 'float'), + 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'], + 'float' + ) + ); + break; + } $this->assertTrue( str_contains($propsFromAnnotations[$fieldCode], 'Money\Money'), sprintf( @@ -252,6 +267,22 @@ protected function assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( ); break; case 'char': + // catalog.document(.element) fields use «char» for plain strings, not Y/N flags + if (in_array($fieldCode, ['commentary', 'currency', 'docType', 'siteId', 'status'], true)) { + $this->assertTrue( + str_contains($propsFromAnnotations[$fieldCode], 'string'), + 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'], + 'string' + ) + ); + break; + } + $this->assertTrue( str_contains($propsFromAnnotations[$fieldCode], 'bool'), sprintf(