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);
+ }
+}