diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index ef49ce2f..10462bb7 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -12,6 +12,7 @@ ->in(__DIR__ . '/src/Services/CRM/Quote/') ->in(__DIR__ . '/src/Services/CRM/Lead/') ->in(__DIR__ . '/src/Services/CRM/Currency/') + ->in(__DIR__ . '/src/Services/CRM/Status/') ->name('*.php') ->exclude(['vendor', 'storage', 'docker', 'docs']) // Exclude directories ->ignoreDotFiles(true) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d77ac4e..f56794d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ ### Added +- Added service `Services\CRM\Status\Service\Status` with support methods, + see [crm.status.* methods](https://github.com/bitrix24/b24phpsdk/issues/194): + - `fields` returns descriptions of reference book fields + - `get` returns an element of the reference book by its identifier + - `list` returns a list of elements of the reference book by filter, with batch calls support + - `add` creates a new element in the specified reference book, with batch calls support + - `delete` deletes an element from the reference book, with batch calls support + - `update` updates an existing element of the reference book, with batch calls support + - `countByFilter` counts elements of the reference book by filter +- Added service `Services\CRM\Status\Service\StatusEntity` with support methods, + - `items` returns elements of the reference book by its symbolic identifier + - `types` returns descriptions of reference book types - Added service `Services\CRM\Lead\Service\LeadContact` with support methods, see [crm.lead.contact.* methods](https://github.com/bitrix24/b24phpsdk/issues/170): - `fields` get fiels for lead contact connection diff --git a/Makefile b/Makefile index c2f32b5c..50c69216 100644 --- a/Makefile +++ b/Makefile @@ -238,6 +238,10 @@ integration_tests_lead_productrows: .PHONY: integration_tests_crm_quote integration_tests_crm_quote: docker-compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_crm_quote + +.PHONY: integration_tests_crm_status +integration_tests_crm_status: + docker-compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_crm_status # work dev environment .PHONY: php-dev-server-up diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 87d2a095..99dc72df 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -26,6 +26,7 @@ parameters: - tests/Integration/Services/CRM/Quote/Service/QuoteProductRowsTest.php - tests/Integration/Services/CRM/Lead/Service/LeadUserfieldTest.php - tests/Integration/Services/CRM/Currency + - tests/Integration/Services/CRM/Status bootstrapFiles: - tests/bootstrap.php parallel: diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 6c5c2c80..e894ee18 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -85,6 +85,9 @@ ./tests/Integration/Services/CRM/Quote/ + + ./tests/Integration/Services/CRM/Status/ + diff --git a/src/Services/CRM/CRMServiceBuilder.php b/src/Services/CRM/CRMServiceBuilder.php index 854c28e0..b36325f8 100644 --- a/src/Services/CRM/CRMServiceBuilder.php +++ b/src/Services/CRM/CRMServiceBuilder.php @@ -528,4 +528,29 @@ public function duplicate(): Duplicates\Service\Duplicate return $this->serviceCache[__METHOD__]; } + + public function status(): Status\Service\Status + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Status\Service\Status( + new Status\Service\Batch($this->batch, $this->log), + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } + + public function statusEntity(): Status\Service\StatusEntity + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Status\Service\StatusEntity( + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } } diff --git a/src/Services/CRM/Status/Result/StatusEntitiesResult.php b/src/Services/CRM/Status/Result/StatusEntitiesResult.php new file mode 100644 index 00000000..c0ac149f --- /dev/null +++ b/src/Services/CRM/Status/Result/StatusEntitiesResult.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\CRM\Status\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class StatusEntitiesResult + * + * @package Bitrix24\SDK\Services\CRM\Status\Result + */ +class StatusEntitiesResult extends AbstractResult +{ + /** + * @return StatusEntityItemResult[] + * @throws BaseException + */ + public function getEntities(): array + { + $res = []; + foreach ($this->getCoreResponse()->getResponseData()->getResult() as $item) { + $res[] = new StatusEntityItemResult($item); + } + + return $res; + } +} diff --git a/src/Services/CRM/Status/Result/StatusEntityItemResult.php b/src/Services/CRM/Status/Result/StatusEntityItemResult.php new file mode 100644 index 00000000..f0cfcfbd --- /dev/null +++ b/src/Services/CRM/Status/Result/StatusEntityItemResult.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Status\Result; + +use Bitrix24\SDK\Services\CRM\Common\Result\AbstractCrmItem; + +/** + * @property-read string $NAME + * @property-read int $SORT + * @property-read string $STATUS_ID + */ +class StatusEntityItemResult extends AbstractCrmItem +{ +} diff --git a/src/Services/CRM/Status/Result/StatusEntityTypeItemResult.php b/src/Services/CRM/Status/Result/StatusEntityTypeItemResult.php new file mode 100644 index 00000000..de77cc59 --- /dev/null +++ b/src/Services/CRM/Status/Result/StatusEntityTypeItemResult.php @@ -0,0 +1,31 @@ + + * + * 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\CRM\Status\Result; + +use Bitrix24\SDK\Services\CRM\Common\Result\AbstractCrmItem; + +/** + * @property-read string $ID + * @property-read string $NAME + * @property-read string $PARENT_ID + * @property-read array $SEMANTIC_INFO + * @property-read int $ENTITY_TYPE_ID + * @property-read string $PREFIX + * @property-read string $FIELD_ATTRIBUTE_SCOPE + * @property-read int $CATEGORY_ID + * @property-read bool $IS_ENABLED + */ +class StatusEntityTypeItemResult extends AbstractCrmItem +{ +} diff --git a/src/Services/CRM/Status/Result/StatusEntityTypesResult.php b/src/Services/CRM/Status/Result/StatusEntityTypesResult.php new file mode 100644 index 00000000..136ab69a --- /dev/null +++ b/src/Services/CRM/Status/Result/StatusEntityTypesResult.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\CRM\Status\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class StatusEntityTypesResult + * + * @package Bitrix24\SDK\Services\CRM\Status\Result + */ +class StatusEntityTypesResult extends AbstractResult +{ + /** + * @return StatusEntityTypeItemResult[] + * @throws BaseException + */ + public function getEntityTypes(): array + { + $res = []; + foreach ($this->getCoreResponse()->getResponseData()->getResult() as $item) { + $res[] = new StatusEntityTypeItemResult($item); + } + + return $res; + } +} diff --git a/src/Services/CRM/Status/Result/StatusItemResult.php b/src/Services/CRM/Status/Result/StatusItemResult.php new file mode 100644 index 00000000..25ac295f --- /dev/null +++ b/src/Services/CRM/Status/Result/StatusItemResult.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Status\Result; + +use Bitrix24\SDK\Services\CRM\Common\Result\AbstractCrmItem; + +/** + * Class StatusItemResult + * + * @property-read int $ID + * @property-read string|null $ENTITY_ID + * @property-read string|null $STATUS_ID + * @property-read int|null $SORT + * @property-read string|null $NAME + * @property-read string|null $NAME_INIT + * @property-read bool|null $SYSTEM + * @property-read int|null $CATEGORY_ID + * @property-read string|null $COLOR + * @property-read bool|null $SEMANTICS + * @property-read array|null $EXTRA + */ +class StatusItemResult extends AbstractCrmItem +{ +} diff --git a/src/Services/CRM/Status/Result/StatusResult.php b/src/Services/CRM/Status/Result/StatusResult.php new file mode 100644 index 00000000..864d0fd9 --- /dev/null +++ b/src/Services/CRM/Status/Result/StatusResult.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\CRM\Status\Result; + +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class StatusResult + * + * @package Bitrix24\SDK\Services\CRM\Status\Result + */ +class StatusResult extends AbstractResult +{ + /** + * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + */ + public function status(): StatusItemResult + { + return new StatusItemResult($this->getCoreResponse()->getResponseData()->getResult()); + } +} diff --git a/src/Services/CRM/Status/Result/StatusesResult.php b/src/Services/CRM/Status/Result/StatusesResult.php new file mode 100644 index 00000000..eb79c056 --- /dev/null +++ b/src/Services/CRM/Status/Result/StatusesResult.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\CRM\Status\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class StatusesResult + * + * @package Bitrix24\SDK\Services\CRM\Status\Result + */ +class StatusesResult extends AbstractResult +{ + /** + * @return StatusItemResult[] + * @throws BaseException + */ + public function getStatuses(): array + { + $items = []; + foreach ($this->getCoreResponse()->getResponseData()->getResult() as $item) { + $items[] = new StatusItemResult($item); + } + + return $items; + } +} diff --git a/src/Services/CRM/Status/Service/Batch.php b/src/Services/CRM/Status/Service/Batch.php new file mode 100644 index 00000000..57a6cbbb --- /dev/null +++ b/src/Services/CRM/Status/Service/Batch.php @@ -0,0 +1,174 @@ + + * + * 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\CRM\Status\Service; + +use Bitrix24\SDK\Attributes\ApiBatchMethodMetadata; +use Bitrix24\SDK\Attributes\ApiBatchServiceMetadata; +use Bitrix24\SDK\Core\Contracts\BatchOperationsInterface; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AddedItemBatchResult; +use Bitrix24\SDK\Core\Result\DeletedItemBatchResult; +use Bitrix24\SDK\Core\Result\UpdatedItemBatchResult; +use Bitrix24\SDK\Services\CRM\Status\Result\StatusItemResult; +use Generator; +use Psr\Log\LoggerInterface; + +#[ApiBatchServiceMetadata(new Scope(['crm']))] +class Batch +{ + /** + * Batch constructor. + */ + public function __construct(protected BatchOperationsInterface $batch, protected LoggerInterface $log) + { + } + + /** + * Batch list method for statuses + * + * @param array{ + * ID?: int, + * ENTITY_ID?: string, + * STATUS_ID?: string, + * SORT?: int, + * NAME?: string, + * NAME_INIT?: string, + * SYSTEM?: bool, + * CATEGORY_ID?: int, + * COLOR?: string, + * SEMANTICS?: string, + * EXTRA?: array, + * } $order + * + * @param array{ + * ID?: int, + * ENTITY_ID?: string, + * STATUS_ID?: string, + * SORT?: int, + * NAME?: string, + * NAME_INIT?: string, + * SYSTEM?: bool, + * CATEGORY_ID?: int, + * COLOR?: string, + * SEMANTICS?: string, + * EXTRA?: array, + * } $filter + * @param array $select = ['ID','ENTITY_ID','STATUS_ID','SORT','NAME','NAME_INIT','SYSTEM','CATEGORY_ID','COLOR','SEMANTICS','EXTRA'] + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'crm.status.list', + 'https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-list.html', + 'Batch list method for statuses' + )] + public function list(array $order, array $filter, array $select, ?int $limit = null): Generator + { + $this->log->debug( + 'batchList', + [ + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'limit' => $limit, + ] + ); + foreach ($this->batch->getTraversableList('crm.status.list', $order, $filter, $select, $limit) as $key => $value) { + yield $key => new StatusItemResult($value); + } + } + + /** + * Batch adding statuses + * + * @param array $statuses + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'crm.status.add', + 'https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-add.html', + 'Batch adding statuses' + )] + public function add(array $statuses): Generator + { + $items = []; + foreach ($statuses as $status) { + $items[] = [ + 'fields' => $status, + ]; + } + + foreach ($this->batch->addEntityItems('crm.status.add', $items) as $key => $item) { + yield $key => new AddedItemBatchResult($item); + } + } + + /** + * Batch update statuses + * + * Update elements in array with structure + * element_id => [ // status id + * 'fields' => [] // status fields to update + * ] + * + * @param array $entityItems + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'crm.status.update', + 'https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-update.html', + 'Update in batch mode a list of statuses' + )] + public function update(array $entityItems): Generator + { + foreach ($this->batch->updateEntityItems('crm.status.update', $entityItems) as $key => $item) { + yield $key => new UpdatedItemBatchResult($item); + } + } + + /** + * Batch delete statuses + * + * @param int[] $statusId + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'crm.status.delete', + 'https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-delete.html', + 'Batch delete statuses' + )] + public function delete(array $statusId): Generator + { + foreach ($this->batch->deleteEntityItems('crm.status.delete', $statusId) as $key => $item) { + yield $key => new DeletedItemBatchResult($item); + } + } +} diff --git a/src/Services/CRM/Status/Service/Status.php b/src/Services/CRM/Status/Service/Status.php new file mode 100644 index 00000000..2b3466b1 --- /dev/null +++ b/src/Services/CRM/Status/Service/Status.php @@ -0,0 +1,242 @@ + + * + * 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\CRM\Status\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\AddedItemResult; +use Bitrix24\SDK\Core\Result\DeletedItemResult; +use Bitrix24\SDK\Core\Result\FieldsResult; +use Bitrix24\SDK\Core\Result\UpdatedItemResult; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\CRM\Status\Result\StatusResult; +use Bitrix24\SDK\Services\CRM\Status\Result\StatusesResult; +use Psr\Log\LoggerInterface; + +#[ApiServiceMetadata(new Scope(['crm']))] +class Status extends AbstractService +{ + /** + * Status constructor. + */ + public function __construct(public Batch $batch, CoreInterface $core, LoggerInterface $logger) + { + parent::__construct($core, $logger); + } + + /** + * Creates a new element in the specified reference book + * + * @link https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-add.html + * + * @param array{ + * ID?: int, + * ENTITY_ID?: string, + * STATUS_ID?: string, + * SORT?: int, + * NAME?: string, + * SYSTEM?: bool, + * CATEGORY_ID?: int, + * COLOR?: string, + * SEMANTICS?: string, + * EXTRA?: array, + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'crm.status.add', + 'https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-add.html', + 'Creates a new element in the specified reference book' + )] + public function add(array $fields): AddedItemResult + { + return new AddedItemResult( + $this->core->call( + 'crm.status.add', + [ + 'fields' => $fields + ] + ) + ); + } + + /** + * Deletes an element from the reference book. + * + * @link https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-delete.html + * + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'crm.status.delete', + 'https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-delete.html', + 'Deletes an element from the reference book' + )] + public function delete(int $id, bool $forced = false): DeletedItemResult + { + $param = $forced ? 'Y' : 'N'; + return new DeletedItemResult( + $this->core->call( + 'crm.status.delete', + [ + 'id' => $id, + 'params' => ['FORCED' => $param] + ] + ) + ); + } + + /** + * Returns descriptions of reference book fields. + * + * @link https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-fields.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'crm.status.fields', + 'https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-fields.html', + 'Returns descriptions of reference book fields' + )] + public function fields(): FieldsResult + { + return new FieldsResult($this->core->call('crm.status.fields')); + } + + /** + * Returns an element of the reference book by its identifier. + * + * @link https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-get.html + * + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'crm.status.get', + 'https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-get.html', + 'Returns an element of the reference book by its identifier' + )] + public function get(int $id): StatusResult + { + return new StatusResult($this->core->call('crm.status.get', ['id' => $id])); + } + + /** + * Returns a list of elements of the reference book by filter. + * + * @link https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-list.html + * + * @param array $order - order of status items + * @param array $filter - filter array + * @param array $select = ['ID','ENTITY_ID','STATUS_ID','SORT','NAME','NAME_INIT','SYSTEM','CATEGORY_ID','COLOR','SEMANTICS','EXTRA'] + * @param integer $startItem - entity number to start from (usually returned in 'next' field of previous 'crm.status.list' API call) + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'crm.status.list', + 'https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-list.html', + 'Returns a list of elements of the reference book by filter' + )] + public function list(array $order = [], array $filter = [], array $select = [], int $startItem = 0): StatusesResult + { + return new StatusesResult( + $this->core->call( + 'crm.status.list', + [ + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'start' => $startItem, + ] + ) + ); + } + + /** + * Updates an existing element of the reference book. + * + * @link https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-update.html + * + * @param array{ + * ID?: int, + * ENTITY_ID?: string, + * STATUS_ID?: string, + * SORT?: int, + * NAME?: string, + * NAME_INIT?: string, + * SYSTEM?: bool, + * CATEGORY_ID?: int, + * COLOR?: string, + * SEMANTICS?: string, + * EXTRA?: array, + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'crm.status.update', + 'https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-update.html', + 'Updates an existing element of the reference book' + )] + public function update(int $id, array $fields): UpdatedItemResult + { + return new UpdatedItemResult( + $this->core->call( + 'crm.status.update', + [ + 'id' => $id, + 'fields' => $fields + ] + ) + ); + } + + /** + * Count statuses by filter + * + * @param array{ + * ID?: int, + * ENTITY_ID?: string, + * STATUS_ID?: string, + * SORT?: int, + * NAME?: string, + * NAME_INIT?: string, + * SYSTEM?: bool, + * CATEGORY_ID?: int, + * COLOR?: string, + * SEMANTICS?: string, + * EXTRA?: array, + * } $filter + * + * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + * @throws \Bitrix24\SDK\Core\Exceptions\TransportException + */ + public function countByFilter(array $filter = []): int + { + return $this->list([], $filter, ['ID'], 1)->getCoreResponse()->getResponseData()->getPagination()->getTotal(); + } +} diff --git a/src/Services/CRM/Status/Service/StatusEntity.php b/src/Services/CRM/Status/Service/StatusEntity.php new file mode 100644 index 00000000..77982bd9 --- /dev/null +++ b/src/Services/CRM/Status/Service/StatusEntity.php @@ -0,0 +1,74 @@ + + * + * 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\CRM\Status\Service; + +use Bitrix24\SDK\Attributes\ApiEndpointMetadata; +use Bitrix24\SDK\Attributes\ApiServiceMetadata; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\CRM\Status\Result\StatusEntitiesResult; +use Bitrix24\SDK\Services\CRM\Status\Result\StatusEntityTypesResult; + +#[ApiServiceMetadata(new Scope(['crm']))] +class StatusEntity extends AbstractService +{ + /** + * Returns elements of the reference book by its symbolic identifier. + * + * @link https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-entity-items.html + * + * @param string $entityId + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'crm.status.entity.items', + 'https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-entity-items.html', + 'Returns elements of the reference book by its symbolic identifier.' + )] + public function items(string $entityId): StatusEntitiesResult + { + return new StatusEntitiesResult( + $this->core->call( + 'crm.status.entity.items', + [ + 'entityId' => $entityId, + ] + ) + ); + } + + + /** + * Returns descriptions of reference book types. + * + * @link https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-entity-types.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'crm.status.entity.types', + 'https://apidocs.bitrix24.com/api-reference/crm/status/crm-status-entity-types.html', + 'Returns descriptions of reference book types.' + )] + public function types(): StatusEntityTypesResult + { + return new StatusEntityTypesResult( + $this->core->call('crm.status.entity.types', []) + ); + } +} diff --git a/tests/CustomAssertions/CustomBitrix24Assertions.php b/tests/CustomAssertions/CustomBitrix24Assertions.php index 2edecd92..5421dba8 100644 --- a/tests/CustomAssertions/CustomBitrix24Assertions.php +++ b/tests/CustomAssertions/CustomBitrix24Assertions.php @@ -348,6 +348,7 @@ protected function assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( case 'crm_multifield': case 'uf_enum_element': case 'currency_localization': + case 'crm_status_extra': $this->assertTrue( str_contains($propsFromAnnotations[$fieldCode], 'array'), sprintf( diff --git a/tests/Integration/Services/CRM/Status/Service/BatchTest.php b/tests/Integration/Services/CRM/Status/Service/BatchTest.php new file mode 100644 index 00000000..88c2d5f6 --- /dev/null +++ b/tests/Integration/Services/CRM/Status/Service/BatchTest.php @@ -0,0 +1,175 @@ + + * + * 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\CRM\Status\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\CRM\Status\Service\Status; +use Bitrix24\SDK\Tests\Integration\Fabric; +use PHPUnit\Framework\TestCase; + +/** + * Class BatchTest + * + * @package Bitrix24\SDK\Tests\Integration\Services\CRM\Status\Service + */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Bitrix24\SDK\Services\CRM\Status\Service\Batch::class)] +class BatchTest extends TestCase +{ + protected Status $statusService; + + + protected function setUp(): void + { + $this->statusService = Fabric::getServiceBuilder()->getCRMScope()->status(); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch list statuses')] + public function testBatchList(): void + { + $newStatus = [ + 'ENTITY_ID' => 'SOURCE', + 'STATUS_ID' => 'Test', + 'SORT' => 1, + 'NAME' => 'Test status', + ]; + $newId = $this->statusService->add($newStatus)->getId(); + + $cnt = 0; + foreach ($this->statusService->batch->list([], ['ID' => $newId], ['ID', 'NAME'], 1) as $item) { + $cnt++; + } + + self::assertGreaterThanOrEqual(1, $cnt); + + $this->statusService->delete($newId); + } + + /** + * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch add status')] + public function testBatchAdd(): void + { + $newStatus = [ + 'ENTITY_ID' => 'SOURCE', + 'STATUS_ID' => 'Test', + 'SORT' => 1, + 'NAME' => 'Test status', + ]; + $items = []; + for ($i = 1; $i < 10; $i++) { + $copy = $newStatus; + $copy['STATUS_ID'] .= $i; + $copy['SORT'] += $i; + $copy['NAME'] .= ' '.$i; + $items[] = $copy; + } + + $cnt = 0; + $itemId = []; + foreach ($this->statusService->batch->add($items) as $item) { + $cnt++; + $itemId[] = $item->getId(); + } + + self::assertEquals(count($items), $cnt); + + $cnt = 0; + foreach ($this->statusService->batch->delete($itemId) as $cnt => $deleteResult) { + $cnt++; + } + + self::assertEquals(count($items), $cnt); + } + + /** + * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch delete statuses')] + public function testBatchDelete(): void + { + $newStatus = [ + 'ENTITY_ID' => 'SOURCE', + 'STATUS_ID' => 'Test', + 'SORT' => 1, + 'NAME' => 'Test status', + ]; + $items = []; + for ($i = 1; $i < 10; $i++) { + $copy = $newStatus; + $copy['STATUS_ID'] .= $i; + $copy['SORT'] += $i; + $copy['NAME'] .= ' '.$i; + $items[] = $copy; + } + + $itemId = []; + foreach ($this->statusService->batch->add($items) as $item) { + $itemId[] = $item->getId(); + } + + $cnt = 0; + foreach ($this->statusService->batch->delete($itemId) as $cnt => $deleteResult) { + $cnt++; + } + + self::assertEquals(count($items), $cnt); + } + + /** + * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + * @throws \Exception + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch update statuses')] + public function testBatchUpdate(): void + { + $newStatus = [ + 'ENTITY_ID' => 'SOURCE', + 'STATUS_ID' => 'Test', + 'SORT' => 1, + 'NAME' => 'Test status', + ]; + $items = []; + for ($i = 1; $i < 10; $i++) { + $copy = $newStatus; + $copy['STATUS_ID'] .= $i; + $copy['SORT'] += $i; + $copy['NAME'] .= ' '.$i; + $items[] = $copy; + } + + $updateStatuses = []; + foreach ($this->statusService->batch->add($items) as $item) { + $id = $item->getId(); + $updateStatuses[$id] = [ + 'fields' => ['NAME' => 'Updated '.$id] + ]; + } + + foreach ($this->statusService->batch->update($updateStatuses) as $statusUpdateResult) { + $this->assertTrue($statusUpdateResult->isSuccess()); + } + + $cnt = 0; + foreach ($this->statusService->batch->delete(array_keys($updateStatuses)) as $cnt => $deleteResult) { + $cnt++; + } + } + +} diff --git a/tests/Integration/Services/CRM/Status/Service/StatusEntityTest.php b/tests/Integration/Services/CRM/Status/Service/StatusEntityTest.php new file mode 100644 index 00000000..55b02550 --- /dev/null +++ b/tests/Integration/Services/CRM/Status/Service/StatusEntityTest.php @@ -0,0 +1,79 @@ + + * + * 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\CRM\Status\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\CRM\Status\Result\StatusEntityItemResult; +use Bitrix24\SDK\Services\CRM\Status\Service\StatusEntity; +use Bitrix24\SDK\Tests\Integration\Fabric; +use PHPUnit\Framework\TestCase; +use Typhoon\Reflection\TyphoonReflector; + +#[\PHPUnit\Framework\Attributes\CoversClass(\Bitrix24\SDK\Services\CRM\Status\Service\StatusEntity::class)] +class StatusEntityTest extends TestCase +{ + private StatusEntity $statusEntityService; + + private TyphoonReflector $typhoonReflector; + + protected function setUp(): void + { + $this->statusEntityService = Fabric::getServiceBuilder()->getCRMScope()->statusEntity(); + $this->typhoonReflector = TyphoonReflector::build(); + } + + public function testItemAllSystemPropertiesAnnotated(): void + { + // get response from server with actual keys + $propListFromApi = array_keys($this->statusEntityService->items('SOURCE')->getCoreResponse()->getResponseData()->getResult()[0]); + // parse keys from phpdoc annotation + $collection = $this->typhoonReflector->reflectClass(StatusEntityItemResult::class)->properties(); + $propsFromAnnotations = []; + foreach ($collection as $meta) { + if ($meta->isAnnotated() && !$meta->isNative()) { + $propsFromAnnotations[] = $meta->id->name; + } + } + + $this->assertEquals($propListFromApi, $propsFromAnnotations, + sprintf('in phpdocs annotations for class %s cant find fields from actual api response: %s', + StatusEntityItemResult::class, + implode(', ', array_values(array_diff($propListFromApi, $propsFromAnnotations))) + )); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testItems(): void + { + $entityId = 'SOURCE'; + + $statusEntitiesResult = $this->statusEntityService->items($entityId); + self::assertIsArray($statusEntitiesResult->getEntities()); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testTypes(): void + { + $statusEntityTypesResult = $this->statusEntityService->types(); + self::assertIsArray($statusEntityTypesResult->getEntityTypes()); + } + +} diff --git a/tests/Integration/Services/CRM/Status/Service/StatusTest.php b/tests/Integration/Services/CRM/Status/Service/StatusTest.php new file mode 100644 index 00000000..bf608a8e --- /dev/null +++ b/tests/Integration/Services/CRM/Status/Service/StatusTest.php @@ -0,0 +1,186 @@ + + * + * 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\CRM\Status\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Core; +use Bitrix24\SDK\Services\CRM\Status\Result\StatusItemResult; +use Bitrix24\SDK\Services\CRM\Status\Service\Status; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Fabric; +use PHPUnit\Framework\Attributes\CoversFunction; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\TestCase; + +/** + * Class StatusTest + * + * @package Bitrix24\SDK\Tests\Integration\Services\CRM\Status\Service + */ +#[CoversMethod(Status::class,'add')] +#[CoversMethod(Status::class,'delete')] +#[CoversMethod(Status::class,'get')] +#[CoversMethod(Status::class,'list')] +#[CoversMethod(Status::class,'fields')] +#[CoversMethod(Status::class,'update')] +#[\PHPUnit\Framework\Attributes\CoversClass(\Bitrix24\SDK\Services\CRM\Status\Service\Status::class)] +class StatusTest extends TestCase +{ + use CustomBitrix24Assertions; + protected Status $statusService; + + protected function setUp(): void + { + $this->statusService = Fabric::getServiceBuilder()->getCRMScope()->status(); + } + + public function testAllSystemFieldsAnnotated(): void + { + $propListFromApi = (new Core\Fields\FieldsFilter())->filterSystemFields(array_keys($this->statusService->fields()->getFieldsDescription())); + $this->assertBitrix24AllResultItemFieldsAnnotated($propListFromApi, StatusItemResult::class); + } + + public function testAllSystemFieldsHasValidTypeAnnotation():void + { + $allFields = $this->statusService->fields()->getFieldsDescription(); + $systemFieldsCodes = (new Core\Fields\FieldsFilter())->filterSystemFields(array_keys($allFields)); + $systemFields = array_filter($allFields, static fn($code): bool => in_array($code, $systemFieldsCodes, true), ARRAY_FILTER_USE_KEY); + + $this->assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( + $systemFields, + StatusItemResult::class); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testAdd(): void + { + $newStatus = [ + 'ENTITY_ID' => 'SOURCE', + 'STATUS_ID' => 'undefined', + 'SORT' => 100, + 'NAME' => 'Test status', + ]; + $newId = $this->statusService->add($newStatus)->getId(); + self::assertGreaterThan(1, $newId); + $this->statusService->delete($newId); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testDelete(): void + { + $newStatus = [ + 'ENTITY_ID' => 'SOURCE', + 'STATUS_ID' => 'undefined', + 'SORT' => 100, + 'NAME' => 'Test status', + ]; + $newId = $this->statusService->add($newStatus)->getId(); + self::assertTrue($this->statusService->delete($newId)->isSuccess()); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testFields(): void + { + self::assertIsArray($this->statusService->fields()->getFieldsDescription()); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testGet(): void + { + $newStatus = [ + 'ENTITY_ID' => 'SOURCE', + 'STATUS_ID' => 'undefined', + 'SORT' => 100, + 'NAME' => 'Test status', + ]; + $newId = $this->statusService->add($newStatus)->getId(); + self::assertGreaterThan( + 1, + $this->statusService->get($newId)->status()->ID + ); + $this->statusService->delete($newId); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testList(): void + { + $newStatus = [ + 'ENTITY_ID' => 'SOURCE', + 'STATUS_ID' => 'undefined', + 'SORT' => 100, + 'NAME' => 'Test status', + ]; + $newId = $this->statusService->add($newStatus)->getId(); + self::assertGreaterThanOrEqual(1, $this->statusService->list([], [], ['ID', 'NAME'])->getStatuses()); + $this->statusService->delete($newId); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testUpdate(): void + { + $newStatus = [ + 'ENTITY_ID' => 'SOURCE', + 'STATUS_ID' => 'undefined', + 'SORT' => 100, + 'NAME' => 'Test status', + ]; + $newId = $this->statusService->add($newStatus)->getId(); + $newName = 'Test2'; + + self::assertTrue($this->statusService->update($newId, ['NAME' => $newName])->isSuccess()); + self::assertEquals($newName, $this->statusService->get($newId)->status()->NAME); + $this->statusService->delete($newId); + } + + /** + * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + * @throws \Bitrix24\SDK\Core\Exceptions\TransportException + */ + public function testCountByFilter(): void + { + $before = $this->statusService->countByFilter(); + + $newStatus = [ + 'ENTITY_ID' => 'SOURCE', + 'STATUS_ID' => 'undefined', + 'SORT' => 100, + 'NAME' => 'Test status', + ]; + $newId = $this->statusService->add($newStatus)->getId(); + + $after = $this->statusService->countByFilter(); + + $this->assertEquals($before + 1, $after); + $this->statusService->delete($newId); + } +}