diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php
index 26d3a1eb..6c868f23 100644
--- a/.php-cs-fixer.php
+++ b/.php-cs-fixer.php
@@ -15,12 +15,12 @@
->in(__DIR__ . '/src/Services/CRM/Requisites/')
->in(__DIR__ . '/src/Services/CRM/Status/')
->in(__DIR__ . '/src/Services/CRM/Timeline/')
+ ->in(__DIR__ . '/src/Services/CRM/Documentgenerator/Numerator/')
->in(__DIR__ . '/src/Services/Entity/Section/')
->in(__DIR__ . '/src/Services/Department/')
->in(__DIR__ . '/src/Services/Paysystem/')
->in(__DIR__ . '/src/Services/Sale/')
->in(__DIR__ . '/src/Services/Task/')
- ->in(__DIR__ . '/src/Services/Sale/')
->name('*.php')
->exclude(['vendor', 'storage', 'docker', 'docs']) // Exclude directories
->ignoreDotFiles(true)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bc877103..756cdacb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,14 @@
### Added
+- Added service `Services\CRM\Documentgenerator\Numerator` with support methods,
+ see [crm.documentgenerator.numerator.* methods](https://github.com/bitrix24/b24phpsdk/issues/215):
+ - `add` adds a new numerator, with batch calls support
+ - `list` gets the list of numerators, with batch calls support
+ - `update` updates an existing numbering with new values, with batch calls support
+ - `delete` deletes a numerator, with batch calls support
+ - `get` gets information about the numerator by its identifier
+ - `count` count numerators
- Added service `Services\Paysystem\Handler\Service\Handler` with support methods,
see [pay_system.handler.* methods](https://github.com/bitrix24/b24phpsdk/issues/260):
- `add` adds a payment system handler
diff --git a/Makefile b/Makefile
index 0e8a2f70..de1df352 100644
--- a/Makefile
+++ b/Makefile
@@ -259,7 +259,7 @@ test-integration-sale-payment-item-shipment:
.PHONY: test-integration-sale-property-relation
test-integration-sale-property-relation:
docker-compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_sale_property_relation
-
+
.PHONY: test-integration-scope-crm
test-integration-scope-crm:
docker-compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_scope_crm
@@ -356,6 +356,10 @@ integration_tests_sale_payment:
integration_tests_sale_payment_item_basket:
docker-compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_sale_payment_item_basket
+.PHONY: integration_tests_crm_documentgenerator_numerator
+integration_tests_crm_documentgenerator_numerator:
+ docker-compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_crm_documentgenerator_numerator
+
# work dev environment
.PHONY: php-dev-server-up
php-dev-server-up:
@@ -411,4 +415,3 @@ build-examples-for-documentation:
--example-template=docs/api/file-templates/examples/master-example.php \
--openai-api-key=$(DOCUMENTATION_OPEN_AI_API_KEY) \
--docs-repo-folder=$(DOCUMENTATION_REPOSITORY_FOLDER)
-
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
index c4d6b7ba..7e824f28 100644
--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -30,6 +30,7 @@ parameters:
- tests/Integration/Services/CRM/Requisites
- tests/Integration/Services/Task
- tests/Integration/Services/Sale
+ - tests/Integration/Services/CRM/Documentgenerator/Numerator
excludePaths:
- tests/Integration/Services/CRM/Requisites/Service/RequisiteUserfieldUseCaseTest.php
- tests/Integration/Services/CRM/Status
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 50f79e31..46101756 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -169,6 +169,9 @@
./tests/Integration/Services/Sale/ShipmentItem/
+
+ ./tests/Integration/Services/CRM/Documentgenerator/Numerator/
+
diff --git a/rector.php b/rector.php
index 892855ab..bd486ee8 100644
--- a/rector.php
+++ b/rector.php
@@ -64,6 +64,8 @@
__DIR__ . '/tests/Integration/Services/Task',
__DIR__ . '/src/Services/Sale',
__DIR__ . '/tests/Integration/Services/Sale',
+ __DIR__ . '/src/Services/CRM/Documentgenerator/Numerator',
+ __DIR__ . '/tests/Integration/Services/CRM/Documentgenerator/Numerator',
__DIR__ . '/tests/Unit/',
])
->withCache(cacheDirectory: __DIR__ . '.cache/rector')
diff --git a/src/Services/CRM/CRMServiceBuilder.php b/src/Services/CRM/CRMServiceBuilder.php
index d72be5ce..b4905fc3 100644
--- a/src/Services/CRM/CRMServiceBuilder.php
+++ b/src/Services/CRM/CRMServiceBuilder.php
@@ -653,4 +653,22 @@ public function timelineBindings(): Timeline\Bindings\Service\Bindings
return $this->serviceCache[__METHOD__];
}
+
+ public function documentgeneratorNumerator(): Documentgenerator\Numerator\Service\Numerator
+ {
+ if (!isset($this->serviceCache[__METHOD__])) {
+ // Use specialized Batch for Numerator to ensure correct REST parameter mapping (e.g., 'id')
+ $numeratorBatch = new Documentgenerator\Numerator\Batch(
+ $this->core,
+ $this->log
+ );
+ $this->serviceCache[__METHOD__] = new Documentgenerator\Numerator\Service\Numerator(
+ new Documentgenerator\Numerator\Service\Batch($numeratorBatch, $this->log),
+ $this->core,
+ $this->log
+ );
+ }
+
+ return $this->serviceCache[__METHOD__];
+ }
}
diff --git a/src/Services/CRM/Documentgenerator/Numerator/Batch.php b/src/Services/CRM/Documentgenerator/Numerator/Batch.php
new file mode 100644
index 00000000..6a9a6c71
--- /dev/null
+++ b/src/Services/CRM/Documentgenerator/Numerator/Batch.php
@@ -0,0 +1,93 @@
+
+ *
+ * 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\Documentgenerator\Numerator;
+
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException;
+use Bitrix24\SDK\Core\Response\DTO\ResponseData;
+use Generator;
+
+/**
+ * Class Batch
+ *
+ * @package Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator
+ */
+class Batch extends \Bitrix24\SDK\Core\Batch
+{
+ /**
+ * Delete entity items with batch call
+ *
+ *
+ * @return Generator|ResponseData[]
+ * @throws \Bitrix24\SDK\Core\Exceptions\BaseException
+ */
+ 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 => $code) {
+ if (!is_int($code)) {
+ throw new InvalidArgumentException(
+ sprintf(
+ 'invalid type «%s» of numerator id «%s» at position %s, id must be integer type',
+ gettype($code),
+ $code,
+ $cnt
+ )
+ );
+ }
+
+ $parameters = ['id' => $code];
+ $this->registerCommand($apiMethod, $parameters);
+ }
+
+ foreach ($this->getTraversable(true) as $cnt => $deletedItemResult) {
+ yield $cnt => $deletedItemResult;
+ }
+ } catch (InvalidArgumentException $exception) {
+ $errorMessage = sprintf('batch delete entity items: %s', $exception->getMessage());
+ $this->logger->error(
+ $errorMessage,
+ [
+ 'trace' => $exception->getTrace(),
+ ]
+ );
+ throw $exception;
+ } catch (\Throwable $exception) {
+ $errorMessage = sprintf('batch delete entity 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/CRM/Documentgenerator/Numerator/Result/AddedNumeratorBatchResult.php b/src/Services/CRM/Documentgenerator/Numerator/Result/AddedNumeratorBatchResult.php
new file mode 100644
index 00000000..f770ba94
--- /dev/null
+++ b/src/Services/CRM/Documentgenerator/Numerator/Result/AddedNumeratorBatchResult.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\CRM\Documentgenerator\Numerator\Result;
+
+use Bitrix24\SDK\Core\Result\AddedItemBatchResult;
+
+/**
+ * Class AddedNumeratorBatchResult
+ *
+ * @package Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Result
+ */
+class AddedNumeratorBatchResult extends AddedItemBatchResult
+{
+ public function getId(): int
+ {
+ return (int)$this->getResponseData()->getResult()['numerator']['id'];
+ }
+}
diff --git a/src/Services/CRM/Documentgenerator/Numerator/Result/AddedNumeratorResult.php b/src/Services/CRM/Documentgenerator/Numerator/Result/AddedNumeratorResult.php
new file mode 100644
index 00000000..ac11cbbe
--- /dev/null
+++ b/src/Services/CRM/Documentgenerator/Numerator/Result/AddedNumeratorResult.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\CRM\Documentgenerator\Numerator\Result;
+
+use Bitrix24\SDK\Core\Result\AddedItemResult;
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+
+/**
+ * Class AddedNumeratorResult
+ *
+ * @package Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Result
+ */
+class AddedNumeratorResult extends AddedItemResult
+{
+ /**
+ * @throws BaseException
+ */
+ public function getId(): int
+ {
+ return (int)$this->getCoreResponse()->getResponseData()->getResult()['numerator']['id'];
+ }
+}
diff --git a/src/Services/CRM/Documentgenerator/Numerator/Result/DeletedNumeratorBatchResult.php b/src/Services/CRM/Documentgenerator/Numerator/Result/DeletedNumeratorBatchResult.php
new file mode 100644
index 00000000..aa7f9f50
--- /dev/null
+++ b/src/Services/CRM/Documentgenerator/Numerator/Result/DeletedNumeratorBatchResult.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\CRM\Documentgenerator\Numerator\Result;
+
+use Bitrix24\SDK\Core\Result\DeletedItemBatchResult;
+
+/**
+ * Class DeletedNumeratorBatchResult
+ *
+ * @package Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Result
+ */
+class DeletedNumeratorBatchResult extends DeletedItemBatchResult
+{
+ public function isSuccess(): bool
+ {
+ return (bool)$this->getResponseData()->getResult();
+ }
+}
diff --git a/src/Services/CRM/Documentgenerator/Numerator/Result/DeletedNumeratorResult.php b/src/Services/CRM/Documentgenerator/Numerator/Result/DeletedNumeratorResult.php
new file mode 100644
index 00000000..f760244f
--- /dev/null
+++ b/src/Services/CRM/Documentgenerator/Numerator/Result/DeletedNumeratorResult.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\Documentgenerator\Numerator\Result;
+
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Result\DeletedItemResult;
+
+/**
+ * Class DeletedNumeratorResult
+ *
+ * @package Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Result
+ */
+class DeletedNumeratorResult extends DeletedItemResult
+{
+ /**
+ * @throws BaseException
+ */
+ public function isSuccess(): bool
+ {
+ return (bool)$this->getCoreResponse()->getResponseData()->getResult();
+ }
+}
diff --git a/src/Services/CRM/Documentgenerator/Numerator/Result/NumeratorItemResult.php b/src/Services/CRM/Documentgenerator/Numerator/Result/NumeratorItemResult.php
new file mode 100644
index 00000000..241cb6d0
--- /dev/null
+++ b/src/Services/CRM/Documentgenerator/Numerator/Result/NumeratorItemResult.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\CRM\Documentgenerator\Numerator\Result;
+
+use Bitrix24\SDK\Services\CRM\Common\Result\AbstractCrmItem;
+
+/**
+ * Class NumeratorItemResult
+ *
+ * @property-read int $id
+ * @property-read string $name
+ * @property-read string $template
+ * @property-read array|null $settings
+ */
+class NumeratorItemResult extends AbstractCrmItem
+{
+}
diff --git a/src/Services/CRM/Documentgenerator/Numerator/Result/NumeratorResult.php b/src/Services/CRM/Documentgenerator/Numerator/Result/NumeratorResult.php
new file mode 100644
index 00000000..717ad45f
--- /dev/null
+++ b/src/Services/CRM/Documentgenerator/Numerator/Result/NumeratorResult.php
@@ -0,0 +1,39 @@
+
+ *
+ * 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\Documentgenerator\Numerator\Result;
+
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Result\AbstractResult;
+
+/**
+ * Class NumeratorResult
+ *
+ * @package Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Result
+ */
+class NumeratorResult extends AbstractResult
+{
+ /**
+ * @throws BaseException
+ */
+ public function numerator(): NumeratorItemResult
+ {
+ $result = $this->getCoreResponse()->getResponseData()->getResult();
+ // Be tolerant to different API payload shapes
+ if (!empty($result['numerator']) && is_array($result['numerator'])) {
+ $result = $result['numerator'];
+ }
+
+ return new NumeratorItemResult($result);
+ }
+}
diff --git a/src/Services/CRM/Documentgenerator/Numerator/Result/NumeratorsResult.php b/src/Services/CRM/Documentgenerator/Numerator/Result/NumeratorsResult.php
new file mode 100644
index 00000000..465674fc
--- /dev/null
+++ b/src/Services/CRM/Documentgenerator/Numerator/Result/NumeratorsResult.php
@@ -0,0 +1,49 @@
+
+ *
+ * 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\Documentgenerator\Numerator\Result;
+
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Result\AbstractResult;
+
+/**
+ * Class NumeratorsResult
+ *
+ * @package Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Result
+ */
+class NumeratorsResult extends AbstractResult
+{
+ /**
+ * @return NumeratorItemResult[]
+ * @throws BaseException
+ */
+ public function getNumerators(): array
+ {
+ $items = [];
+ $source = [];
+
+ $result = $this->getCoreResponse()->getResponseData()->getResult();
+
+ if (!empty($result['numerators']) && is_array($result['numerators'])) {
+ $source = $result['numerators'];
+ } elseif (!empty($result['items']) && is_array($result['items'])) {
+ $source = $result['items'];
+ }
+
+ foreach ($source as $item) {
+ $items[] = new NumeratorItemResult($item);
+ }
+
+ return $items;
+ }
+}
diff --git a/src/Services/CRM/Documentgenerator/Numerator/Result/UpdatedNumeratorBatchResult.php b/src/Services/CRM/Documentgenerator/Numerator/Result/UpdatedNumeratorBatchResult.php
new file mode 100644
index 00000000..b73b514c
--- /dev/null
+++ b/src/Services/CRM/Documentgenerator/Numerator/Result/UpdatedNumeratorBatchResult.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\CRM\Documentgenerator\Numerator\Result;
+
+use Bitrix24\SDK\Core\Result\UpdatedItemBatchResult;
+
+/**
+ * Class UpdatedNumeratorBatchResult
+ *
+ * @package Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Result
+ */
+class UpdatedNumeratorBatchResult extends UpdatedItemBatchResult
+{
+ public function isSuccess(): bool
+ {
+ return (bool)$this->getResponseData()->getResult();
+ }
+}
diff --git a/src/Services/CRM/Documentgenerator/Numerator/Result/UpdatedNumeratorResult.php b/src/Services/CRM/Documentgenerator/Numerator/Result/UpdatedNumeratorResult.php
new file mode 100644
index 00000000..fbf08705
--- /dev/null
+++ b/src/Services/CRM/Documentgenerator/Numerator/Result/UpdatedNumeratorResult.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\Documentgenerator\Numerator\Result;
+
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Result\UpdatedItemResult;
+
+/**
+ * Class UpdatedNumeratorResult
+ *
+ * @package Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Result
+ */
+class UpdatedNumeratorResult extends UpdatedItemResult
+{
+ /**
+ * @throws BaseException
+ */
+ public function isSuccess(): bool
+ {
+ return (bool)$this->getCoreResponse()->getResponseData()->getResult();
+ }
+}
diff --git a/src/Services/CRM/Documentgenerator/Numerator/Service/Batch.php b/src/Services/CRM/Documentgenerator/Numerator/Service/Batch.php
new file mode 100644
index 00000000..1139c41c
--- /dev/null
+++ b/src/Services/CRM/Documentgenerator/Numerator/Service/Batch.php
@@ -0,0 +1,156 @@
+
+ *
+ * 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\Documentgenerator\Numerator\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\Services\CRM\Documentgenerator\Numerator\Result\AddedNumeratorBatchResult;
+use Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Result\UpdatedNumeratorBatchResult;
+use Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Result\DeletedNumeratorBatchResult;
+use Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Result\NumeratorItemResult;
+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 numerators
+ *
+ * @return Generator
+ * @throws BaseException
+ */
+ #[ApiBatchMethodMetadata(
+ 'crm.documentgenerator.numerator.list',
+ 'https://apidocs.bitrix24.com/api-reference/crm/document-generator/numerator/crm-document-generator-numerator-list.html',
+ 'Batch list method for numerators'
+ )]
+ public function list(?int $limit = null): Generator
+ {
+ $this->log->debug(
+ 'batchList',
+ [
+ 'limit' => $limit,
+ ]
+ );
+
+ // Use pagination-based traversable to avoid dependency on element ID field name
+ $numeratorListGenerator = $this->batch->getTraversableListWithCount(
+ 'crm.documentgenerator.numerator.list',
+ [],
+ [],
+ [],
+ $limit
+ );
+ foreach ($numeratorListGenerator as $key => $value) {
+ yield $key => new NumeratorItemResult($value);
+ }
+ }
+
+ /**
+ * Batch adding numerators
+ *
+ * @param array $numerators
+ *
+ * @return Generator
+ * @throws BaseException
+ */
+ #[ApiBatchMethodMetadata(
+ 'crm.documentgenerator.numerator.add',
+ 'https://apidocs.bitrix24.com/api-reference/crm/document-generator/numerator/crm-document-generator-numerator-add.html',
+ 'Batch adding numerators'
+ )]
+ public function add(array $numerators): Generator
+ {
+ $items = [];
+ foreach ($numerators as $item) {
+ $items[] = [
+ 'fields' => $item,
+ ];
+ }
+
+ foreach ($this->batch->addEntityItems('crm.documentgenerator.numerator.add', $items) as $key => $item) {
+ yield $key => new AddedNumeratorBatchResult($item);
+ }
+ }
+
+ /**
+ * Batch update numerators
+ *
+ * Update elements in array with structure
+ * id => [ // Numerator id
+ * 'fields' => [] // Numerator fields to update
+ * ]
+ *
+ * @param array $entityItems
+ *
+ * @return Generator
+ * @throws BaseException
+ */
+ #[ApiBatchMethodMetadata(
+ 'crm.documentgenerator.numerator.update',
+ 'https://apidocs.bitrix24.com/api-reference/crm/document-generator/numerator/crm-document-generator-numerator-update.html',
+ 'Update in batch mode a list of numerators'
+ )]
+ public function update(array $entityItems): Generator
+ {
+ foreach (
+ $this->batch->updateEntityItems(
+ 'crm.documentgenerator.numerator.update',
+ $entityItems
+ ) as $key => $item
+ ) {
+ yield $key => new UpdatedNumeratorBatchResult($item);
+ }
+ }
+
+ /**
+ * Batch delete numerators
+ *
+ * @param int[] $numeratorId
+ *
+ * @return Generator
+ * @throws BaseException
+ */
+ #[ApiBatchMethodMetadata(
+ 'crm.documentgenerator.numerator.delete',
+ 'https://apidocs.bitrix24.com/api-reference/crm/document-generator/numerator/crm-document-generator-numerator-delete.html',
+ 'Batch delete numerators'
+ )]
+ public function delete(array $numeratorId): Generator
+ {
+ foreach (
+ $this->batch->deleteEntityItems(
+ 'crm.documentgenerator.numerator.delete',
+ $numeratorId
+ ) as $key => $item
+ ) {
+ yield $key => new DeletedNumeratorBatchResult($item);
+ }
+ }
+}
diff --git a/src/Services/CRM/Documentgenerator/Numerator/Service/Numerator.php b/src/Services/CRM/Documentgenerator/Numerator/Service/Numerator.php
new file mode 100644
index 00000000..a0f73eb5
--- /dev/null
+++ b/src/Services/CRM/Documentgenerator/Numerator/Service/Numerator.php
@@ -0,0 +1,192 @@
+
+ *
+ * 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\Documentgenerator\Numerator\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\Services\AbstractService;
+use Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Result\AddedNumeratorResult;
+use Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Result\DeletedNumeratorResult;
+use Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Result\NumeratorResult;
+use Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Result\NumeratorsResult;
+use Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Result\UpdatedNumeratorResult;
+use Psr\Log\LoggerInterface;
+
+#[ApiServiceMetadata(new Scope(['crm']))]
+class Numerator extends AbstractService
+{
+ /**
+ * Numerator constructor
+ */
+ public function __construct(public Batch $batch, CoreInterface $core, LoggerInterface $logger)
+ {
+ parent::__construct($core, $logger);
+ }
+
+ /**
+ * Adds a new numerator
+ *
+ * @link https://apidocs.bitrix24.com/api-reference/crm/document-generator/numerator/crm-document-generator-numerator-add.html
+ *
+ * @param array{
+ * name: string,
+ * template: string,
+ * settings?: array
+ * } $fields
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[ApiEndpointMetadata(
+ 'crm.documentgenerator.numerator.add',
+ 'https://apidocs.bitrix24.com/api-reference/crm/document-generator/numerator/crm-document-generator-numerator-add.html',
+ 'Adds a new numerator'
+ )]
+ public function add(array $fields): AddedNumeratorResult
+ {
+ return new AddedNumeratorResult(
+ $this->core->call(
+ 'crm.documentgenerator.numerator.add',
+ [
+ 'fields' => $fields
+ ]
+ )
+ );
+ }
+
+ /**
+ * Removes a numerator
+ *
+ * @link https://apidocs.bitrix24.com/api-reference/crm/document-generator/numerator/crm-document-generator-numerator-delete.html
+ *
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[ApiEndpointMetadata(
+ 'crm.documentgenerator.numerator.delete',
+ 'https://apidocs.bitrix24.com/api-reference/crm/document-generator/numerator/crm-document-generator-numerator-delete.html',
+ 'Removes a numerator'
+ )]
+ public function delete(int $id): DeletedNumeratorResult
+ {
+ $params = [
+ 'id' => $id,
+ ];
+
+ return new DeletedNumeratorResult(
+ $this->core->call(
+ 'crm.documentgenerator.numerator.delete',
+ $params
+ )
+ );
+ }
+
+ /**
+ * Returns information about the numerator by its identifier
+ *
+ * @link https://apidocs.bitrix24.com/api-reference/crm/document-generator/numerator/crm-document-generator-numerator-get.html
+ *
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[ApiEndpointMetadata(
+ 'crm.documentgenerator.numerator.get',
+ 'https://apidocs.bitrix24.com/api-reference/crm/document-generator/numerator/crm-document-generator-numerator-get.html',
+ 'Returns information about the numerator by its identifier'
+ )]
+ public function get(int $id): NumeratorResult
+ {
+ return new NumeratorResult($this->core->call('crm.documentgenerator.numerator.get', ['id' => $id]));
+ }
+
+ /**
+ * Returns a list of numerators
+ *
+ * @link https://apidocs.bitrix24.com/api-reference/crm/document-generator/numerator/crm-document-generator-numerator-list.html
+ *
+ * @param int $start - offset for pagination
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[ApiEndpointMetadata(
+ 'crm.documentgenerator.numerator.list',
+ 'https://apidocs.bitrix24.com/api-reference/crm/document-generator/numerator/crm-document-generator-numerator-list.html',
+ 'Returns a list of numerators'
+ )]
+ public function list(int $start = 0): NumeratorsResult
+ {
+ return new NumeratorsResult(
+ $this->core->call(
+ 'crm.documentgenerator.numerator.list',
+ [
+ 'start' => $start
+ ]
+ )
+ );
+ }
+
+ /**
+ * Updates an existing numbering with new values
+ *
+ * The method crm.documentgenerator.numerator.update updates an existing numbering with new values
+ *
+ * @link https://apidocs.bitrix24.com/api-reference/crm/document-generator/numerator/crm-document-generator-numerator-update.html
+ *
+ * @param array{
+ * name: string,
+ * template: string,
+ * settings?: array
+ * } $fields
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[ApiEndpointMetadata(
+ 'crm.documentgenerator.numerator.update',
+ 'https://apidocs.bitrix24.com/api-reference/crm/document-generator/numerator/crm-document-generator-numerator-update.html',
+ 'Updates an existing numbering with new values'
+ )]
+ public function update(int $id, array $fields): UpdatedNumeratorResult
+ {
+ $params = [
+ 'id' => $id,
+ 'fields' => $fields
+ ];
+
+ return new UpdatedNumeratorResult(
+ $this->core->call(
+ 'crm.documentgenerator.numerator.update',
+ $params
+ )
+ );
+ }
+
+ /**
+ * Count numerators
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function count(): int
+ {
+ return $this->list()->getCoreResponse()->getResponseData()->getPagination()->getTotal();
+ }
+}
diff --git a/tests/Integration/Services/CRM/Documentgenerator/Numerator/Service/BatchTest.php b/tests/Integration/Services/CRM/Documentgenerator/Numerator/Service/BatchTest.php
new file mode 100644
index 00000000..f9a8f270
--- /dev/null
+++ b/tests/Integration/Services/CRM/Documentgenerator/Numerator/Service/BatchTest.php
@@ -0,0 +1,165 @@
+
+ * 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\Documentgenerator\Numerator\Service;
+
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException;
+use Bitrix24\SDK\Core\Exceptions\TransportException;
+use Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Service\Numerator;
+use Bitrix24\SDK\Tests\Integration\Fabric;
+use PHPUnit\Framework\TestCase;
+use Faker;
+
+/**
+ * Class BatchTest
+ *
+ * @package Bitrix24\SDK\Tests\Integration\Services\CRM\Documentgenerator\Numerator\Service
+ */
+#[\PHPUnit\Framework\Attributes\CoversClass(\Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Service\Batch::class)]
+class BatchTest extends TestCase
+{
+ protected Numerator $numeratorService;
+
+ private Faker\Generator $faker;
+
+ /**
+ * @throws InvalidArgumentException
+ */
+ protected function setUp(): void
+ {
+ $this->numeratorService = Fabric::getServiceBuilder()->getCRMScope()->documentgeneratorNumerator();
+ $this->faker = Faker\Factory::create();
+ }
+
+ /**
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[\PHPUnit\Framework\Attributes\TestDox('Batch list numerators')]
+ public function testBatchList(): void
+ {
+ $id = $this->numeratorService->add([
+ 'name' => 'num-' . $this->faker->uuid(),
+ 'template' => 'BL-{NUMBER}',
+ ])->getId();
+
+ $cnt = 0;
+ foreach ($this->numeratorService->batch->list(1) as $item) {
+ $cnt++;
+ }
+
+ self::assertGreaterThanOrEqual(1, $cnt);
+
+ // Cleanup
+ $this->numeratorService->delete($id);
+ }
+
+ /**
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[\PHPUnit\Framework\Attributes\TestDox('Batch add numerators')]
+ public function testBatchAdd(): void
+ {
+ $items = [];
+ for ($i = 1; $i <= 5; $i++) {
+ $items[] = [
+ 'name' => 'num-' . $this->faker->uuid(),
+ 'template' => 'BA-{NUMBER}',
+ ];
+ }
+
+ $ids = [];
+ $cnt = 0;
+ foreach ($this->numeratorService->batch->add($items) as $added) {
+ $cnt++;
+ $ids[] = $added->getId();
+ }
+
+ self::assertEquals(count($items), $cnt);
+
+ $delCnt = 0;
+ foreach ($this->numeratorService->batch->delete($ids) as $deleted) {
+ $delCnt++;
+ }
+
+ self::assertEquals(count($items), $delCnt);
+ }
+
+ /**
+ * @throws BaseException
+ */
+ #[\PHPUnit\Framework\Attributes\TestDox('Batch delete numerators')]
+ public function testBatchDelete(): void
+ {
+ $items = [];
+ for ($i = 1; $i <= 5; $i++) {
+ $items[] = [
+ 'name' => 'num-' . $this->faker->uuid(),
+ 'template' => 'BD-{NUMBER}',
+ ];
+ }
+
+ $ids = [];
+ foreach ($this->numeratorService->batch->add($items) as $added) {
+ $ids[] = $added->getId();
+ }
+
+ $delCnt = 0;
+ foreach ($this->numeratorService->batch->delete($ids) as $deleted) {
+ $delCnt++;
+ }
+
+ self::assertEquals(count($items), $delCnt);
+ }
+
+ /**
+ * @throws BaseException
+ */
+ #[\PHPUnit\Framework\Attributes\TestDox('Batch update numerators')]
+ public function testBatchUpdate(): void
+ {
+ $items = [];
+ for ($i = 1; $i <= 5; $i++) {
+ $items[] = [
+ 'name' => 'num-' . $this->faker->uuid(),
+ 'template' => 'BU-{NUMBER}',
+ ];
+ }
+
+ $updatePayload = [];
+ foreach ($this->numeratorService->batch->add($items) as $added) {
+ $id = $added->getId();
+ $updatePayload[$id] = [
+ 'fields' => [
+ 'name' => 'updated-' . $id,
+ 'template' => 'BU2-{NUMBER}',
+ ],
+ ];
+ }
+
+ foreach ($this->numeratorService->batch->update($updatePayload) as $updated) {
+ $this->assertTrue($updated->isSuccess());
+ }
+
+ // Cleanup
+ $ids = array_keys($updatePayload);
+ $deletedCount = 0;
+ foreach ($this->numeratorService->batch->delete($ids) as $deleted) {
+ $deletedCount++;
+ }
+
+ self::assertEquals(count($ids), $deletedCount);
+
+ self::assertTrue(true);
+ }
+}
diff --git a/tests/Integration/Services/CRM/Documentgenerator/Numerator/Service/NumeratorTest.php b/tests/Integration/Services/CRM/Documentgenerator/Numerator/Service/NumeratorTest.php
new file mode 100644
index 00000000..26485191
--- /dev/null
+++ b/tests/Integration/Services/CRM/Documentgenerator/Numerator/Service/NumeratorTest.php
@@ -0,0 +1,173 @@
+
+ *
+ * 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\Documentgenerator\Numerator\Service;
+
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException;
+use Bitrix24\SDK\Core\Exceptions\TransportException;
+use Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Result\NumeratorItemResult;
+use Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Service\Numerator;
+use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions;
+use Bitrix24\SDK\Tests\Integration\Fabric;
+use PHPUnit\Framework\Attributes\CoversMethod;
+use PHPUnit\Framework\TestCase;
+use Faker;
+
+/**
+ * Class NumeratorTest
+ *
+ * @package Bitrix24\SDK\Tests\Integration\Services\CRM\Documentgenerator\Numerator\Service
+ */
+#[CoversMethod(Numerator::class, 'add')]
+#[CoversMethod(Numerator::class, 'delete')]
+#[CoversMethod(Numerator::class, 'get')]
+#[CoversMethod(Numerator::class, 'list')]
+#[CoversMethod(Numerator::class, 'update')]
+#[\PHPUnit\Framework\Attributes\CoversClass(\Bitrix24\SDK\Services\CRM\Documentgenerator\Numerator\Service\Numerator::class)]
+class NumeratorTest extends TestCase
+{
+ use CustomBitrix24Assertions;
+
+ private Numerator $numeratorService;
+
+ private Faker\Generator $faker;
+
+ /**
+ * @throws InvalidArgumentException
+ */
+ protected function setUp(): void
+ {
+ $this->numeratorService = Fabric::getServiceBuilder()->getCRMScope()->documentgeneratorNumerator();
+ $this->faker = Faker\Factory::create();
+ }
+
+ /**
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testAdd(): void
+ {
+ $name = 'num-' . $this->faker->uuid();
+ $id = $this->numeratorService->add([
+ 'name' => $name,
+ 'template' => 'N-{NUMBER}',
+ ])->getId();
+
+ self::assertGreaterThanOrEqual(1, $id);
+
+ // Cleanup
+ $this->numeratorService->delete($id);
+ }
+
+ /**
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testGet(): void
+ {
+ $name = 'num-' . $this->faker->uuid();
+ $id = $this->numeratorService->add([
+ 'name' => $name,
+ 'template' => 'G-{NUMBER}',
+ ])->getId();
+
+ $numeratorItemResult = $this->numeratorService->get($id)->numerator();
+ self::assertInstanceOf(NumeratorItemResult::class, $numeratorItemResult);
+ self::assertEquals($id, $numeratorItemResult->id);
+ self::assertEquals($name, $numeratorItemResult->name);
+
+ // Cleanup
+ $this->numeratorService->delete($id);
+ }
+
+ /**
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testList(): void
+ {
+ $name = 'num-' . $this->faker->uuid();
+ $id = $this->numeratorService->add([
+ 'name' => $name,
+ 'template' => 'L-{NUMBER}',
+ ])->getId();
+
+ $list = $this->numeratorService->list()->getNumerators();
+ self::assertIsArray($list);
+ self::assertGreaterThanOrEqual(1, count($list));
+
+ // Cleanup
+ $this->numeratorService->delete($id);
+ }
+
+ /**
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testUpdate(): void
+ {
+ $name = 'num-' . $this->faker->uuid();
+ $id = $this->numeratorService->add([
+ 'name' => $name,
+ 'template' => 'U-{NUMBER}',
+ ])->getId();
+
+ $newName = $name . '-updated';
+ self::assertTrue(
+ $this->numeratorService->update($id, [
+ 'name' => $newName,
+ 'template' => 'U2-{NUMBER}',
+ ])->isSuccess()
+ );
+
+ self::assertEquals($newName, $this->numeratorService->get($id)->numerator()->name);
+
+ // Cleanup
+ $this->numeratorService->delete($id);
+ }
+
+ /**
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testDelete(): void
+ {
+ $id = $this->numeratorService->add([
+ 'name' => 'num-' . $this->faker->uuid(),
+ 'template' => 'D-{NUMBER}',
+ ])->getId();
+
+ self::assertTrue($this->numeratorService->delete($id)->isSuccess());
+ }
+
+ /**
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testCount(): void
+ {
+ $countBefore = $this->numeratorService->count();
+
+ $id = $this->numeratorService->add([
+ 'name' => 'num-' . $this->faker->uuid(),
+ 'template' => 'C-{NUMBER}',
+ ])->getId();
+
+ $countAfter = $this->numeratorService->count();
+ self::assertEquals($countBefore + 1, $countAfter);
+
+ // Cleanup
+ $this->numeratorService->delete($id);
+ }
+}