diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 05755d8d..b1ab61a9 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -18,6 +18,7 @@ ->in(__DIR__ . '/src/Services/CRM/Documentgenerator/Numerator/') ->in(__DIR__ . '/src/Services/CRM/Documentgenerator/Document/') ->in(__DIR__ . '/src/Services/CRM/Documentgenerator/Template/') + ->in(__DIR__ . '/src/Services/Documentgenerator/') ->in(__DIR__ . '/src/Services/Entity/Section/') ->in(__DIR__ . '/src/Services/Department/') ->in(__DIR__ . '/src/Services/Landing/') diff --git a/.tasks/489/plan.md b/.tasks/489/plan.md new file mode 100644 index 00000000..b6ae7488 --- /dev/null +++ b/.tasks/489/plan.md @@ -0,0 +1,190 @@ +# Plan: Add support for documentgenerator.document.* and documentgenerator.template.* methods (issue #489) + +## Context + +This issue adds SDK support for the `documentgenerator.document.*` REST API methods — the +**non-CRM** Document Generator scope. Unlike `crm.documentgenerator.document.*`, these methods +work with any data provider, not just CRM entities. + +Key differences from the CRM scope (`src/Services/CRM/Documentgenerator/Document/`): + +| Aspect | CRM scope | documentgenerator scope | +|---|---|---| +| Method prefix | `crm.documentgenerator.document.` | `documentgenerator.document.` | +| `add` params | `templateId`, `entityTypeId`, `entityId` | `templateId`, `providerClassName`, `value` | +| `update` extra params | `values`, `stampsEnabled` | `values`, `fields`, `stampsEnabled` | +| SDK scope | `['crm']` | `['documentgenerator']` | +| Builder access | `getCRMScope()->documentgeneratorDocument()` | `getDocumentgeneratorScope()->document()` | + +Response shape for `list`: `result.documents[]` (same key as CRM). +Response shape for `get/add`: `result.document{}` (same key as CRM). + +Custom `Batch` class is required because the API uses lowercase `id` for delete/update and +wraps list results under the `documents` key (same as CRM version). + +--- + +## Files Created + +### Source files +1. `src/Services/Documentgenerator/Document/Result/DocumentItemResult.php` — item result with field type casting +2. `src/Services/Documentgenerator/Document/Result/DocumentResult.php` — single document result +3. `src/Services/Documentgenerator/Document/Result/DocumentsResult.php` — list of documents result +4. `src/Services/Documentgenerator/Document/Result/AddedDocumentResult.php` — add result +5. `src/Services/Documentgenerator/Document/Result/AddedDocumentBatchResult.php` — batch add result +6. `src/Services/Documentgenerator/Document/Result/DeletedDocumentResult.php` — delete result +7. `src/Services/Documentgenerator/Document/Result/DeletedDocumentBatchResult.php` — batch delete result +8. `src/Services/Documentgenerator/Document/Result/UpdatedDocumentResult.php` — update result +9. `src/Services/Documentgenerator/Document/Result/UpdatedDocumentBatchResult.php` — batch update result +10. `src/Services/Documentgenerator/Document/Result/DocumentFieldsResult.php` — getFields result +11. `src/Services/Documentgenerator/Document/Result/PublicUrlResult.php` — enablePublicUrl result +12. `src/Services/Documentgenerator/Document/Batch.php` — custom Batch override (lowercase id, documents wrapper) +13. `src/Services/Documentgenerator/Document/Service/Batch.php` — service-level batch wrapper +14. `src/Services/Documentgenerator/Document/Service/Document.php` — main service class +15. `src/Services/Documentgenerator/DocumentgeneratorServiceBuilder.php` — scope builder + +### Test files +16. `tests/Integration/Services/Documentgenerator/Document/Service/DocumentTest.php` +17. `tests/Integration/Services/Documentgenerator/Document/Service/BatchTest.php` + +--- + +## Files Modified + +### 1. `src/Services/ServiceBuilder.php` +- Added `use Bitrix24\SDK\Services\Documentgenerator\DocumentgeneratorServiceBuilder;` +- Added `getDocumentgeneratorScope(): DocumentgeneratorServiceBuilder` method + +### 2. `rector.php` +- Added paths for `src/Services/Documentgenerator` and `tests/Integration/Services/Documentgenerator` + +### 3. `phpunit.xml.dist` +- Added `integration_tests_scope_documentgenerator` and `integration_tests_documentgenerator_document` test suites + +### 4. `Makefile` +- Added `integration_tests_scope_documentgenerator` and `integration_tests_documentgenerator_document` targets + +### 5. `CHANGELOG.md` +- Added entry under `## 3.3.0 – UNRELEASED → ### Added` + +--- + +## Deptrac compliance + +New code lives in `src/Services/Documentgenerator/` which belongs to the `Services` layer. +It depends only on `Core` (AbstractItem, AbstractResult, AddedItemResult, etc.). No new violations. + +--- + +## Verification + +```bash +make lint-rector +make lint-phpstan +make lint-deptrac +make test-unit +make integration_tests_documentgenerator_document +make integration_tests_documentgenerator_template +make integration_tests_documentgenerator_template_annotations +``` + +--- + +## Phase 2: documentgenerator.template.* (added 2026-05-26) + +Template methods are implemented in `src/Services/Documentgenerator/Template/`. + +Key differences from CRM variant: +- `getFields` requires only `id` (no `entityTypeId`) +- `add` supports `code` and `fileId` fields +- `update` supports `providers` in fields +- List response: `result.templates` keyed by id +- Single-item response: `result.template` +- Template fields response: `result.templateFields` + +### Files Created (Phase 2) + +1. `src/Services/Documentgenerator/Template/Result/TemplateItemResult.php` +2. `src/Services/Documentgenerator/Template/Result/TemplateResult.php` +3. `src/Services/Documentgenerator/Template/Result/TemplatesResult.php` +4. `src/Services/Documentgenerator/Template/Result/AddedTemplateResult.php` +5. `src/Services/Documentgenerator/Template/Result/UpdatedTemplateResult.php` +6. `src/Services/Documentgenerator/Template/Result/DeletedTemplateResult.php` +7. `src/Services/Documentgenerator/Template/Result/AddedTemplateBatchResult.php` +8. `src/Services/Documentgenerator/Template/Result/UpdatedTemplateBatchResult.php` +9. `src/Services/Documentgenerator/Template/Result/DeletedTemplateBatchResult.php` +10. `src/Services/Documentgenerator/Template/Result/TemplateFieldsResult.php` +11. `src/Services/Documentgenerator/Template/Batch.php` +12. `src/Services/Documentgenerator/Template/Service/Batch.php` +13. `src/Services/Documentgenerator/Template/Service/Template.php` +14. `tests/Integration/Services/Documentgenerator/Template/Service/TemplateTest.php` +15. `tests/Integration/Services/Documentgenerator/Template/Service/BatchTest.php` +16. `tests/Integration/Services/Documentgenerator/Template/Result/TemplateItemResultAnnotationsTest.php` + +### Files Modified (Phase 2) + +- `src/Services/Documentgenerator/DocumentgeneratorServiceBuilder.php` — added `template()` method +- `phpunit.xml.dist` — added 3 new test suites for template +- `Makefile` — added 3 new make targets +- `.php-cs-fixer.php` — added `src/Services/Documentgenerator/` +- `phpstan.neon.dist` — added `tests/Integration/Services/Documentgenerator` +- `CHANGELOG.md` — added Template entry under `## 3.3.0 – UNRELEASED` + +--- + +## Plan: Add support for documentgenerator.region.* methods (issue #489) + +## Context + +The Bitrix24 REST API exposes a set of methods for managing document generator regions: +- `documentgenerator.region.add` — creates a new custom region +- `documentgenerator.region.update` — updates an existing region by `id` + `fields` +- `documentgenerator.region.get` — returns a region by `id` +- `documentgenerator.region.list` — returns a paginated list of regions +- `documentgenerator.region.delete` — deletes a region by `id` (returns null on success) + +All methods belong to scope `documentgenerator`. + +API response envelope (verified against `documentgenerator.region.delete` via MCP): +- Add → `result.region = {...}` (matching pattern of numerator.add) +- Update → `result = null` (boolean cast = true on success) +- Get → `result.region = {...}` +- List → `result.regions = [...]` +- Delete → `result = null` (boolean cast on result) + +Region entity fields (based on API docs): +- `id` — int +- `languageId` — string +- `name` — string +- `code` — string + +All REST methods use lowercase `id` parameter (not `ID`), matching the Numerator pattern. +A custom `Batch` class (like `Numerator\Batch`) is required to override lowercase `id` +and `regions` result key handling. + +--- + +## Files to Create + +- `src/Services/Documentgenerator/Region/Result/RegionItemResult.php` +- `src/Services/Documentgenerator/Region/Result/RegionResult.php` +- `src/Services/Documentgenerator/Region/Result/RegionsResult.php` +- `src/Services/Documentgenerator/Region/Result/AddedRegionResult.php` +- `src/Services/Documentgenerator/Region/Result/AddedRegionBatchResult.php` +- `src/Services/Documentgenerator/Region/Result/UpdatedRegionResult.php` +- `src/Services/Documentgenerator/Region/Result/UpdatedRegionBatchResult.php` +- `src/Services/Documentgenerator/Region/Result/DeletedRegionResult.php` +- `src/Services/Documentgenerator/Region/Result/DeletedRegionBatchResult.php` +- `src/Services/Documentgenerator/Region/Batch.php` +- `src/Services/Documentgenerator/Region/Service/Batch.php` +- `src/Services/Documentgenerator/Region/Service/Region.php` +- `tests/Integration/Services/Documentgenerator/Region/Service/RegionTest.php` +- `tests/Integration/Services/Documentgenerator/Region/Service/BatchTest.php` +- `tests/Integration/Services/Documentgenerator/Region/Result/RegionItemResultAnnotationsTest.php` + +## Files to Modify + +- `src/Services/Documentgenerator/DocumentgeneratorServiceBuilder.php` +- `phpunit.xml.dist` +- `Makefile` +- `CHANGELOG.md` diff --git a/CHANGELOG.md b/CHANGELOG.md index bf21c905..26c0acc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,51 @@ ### Added +- Added service `Services\Documentgenerator\Role` with support for `documentgenerator.role.*` methods, + see [documentgenerator.role.* methods](https://apidocs.bitrix24.com/api-reference/document-generator/role/index.html) ([#489](https://github.com/bitrix24/b24phpsdk/issues/489)): + - `add` creates a new role, with batch calls support + - `list` gets the list of roles, with batch calls support + - `update` updates an existing role, with batch calls support + - `delete` deletes a role, with batch calls support + - `get` gets information about the role by its identifier (includes permissions) + - `fillAccesses` completely replaces the role-to-access-code binding map + - `count` counts roles +- Added service `Services\Documentgenerator\Region` with support for `documentgenerator.region.*` methods, + see [documentgenerator.region.* methods](https://apidocs.bitrix24.com/api-reference/document-generator/region/index.html) ([#489](https://github.com/bitrix24/b24phpsdk/issues/489)): + - `add` creates a new region, with batch calls support + - `list` gets the list of regions, with batch calls support + - `update` updates an existing region, with batch calls support + - `delete` deletes a region, with batch calls support + - `get` gets information about the region by its identifier + - `count` counts regions +- Added service `Services\Documentgenerator\Numerator` with support for `documentgenerator.numerator.*` methods, + see [documentgenerator.numerator.* methods](https://apidocs.bitrix24.com/api-reference/document-generator/numerators/index.html) ([#489](https://github.com/bitrix24/b24phpsdk/issues/489)): + - `add` creates a new numerator, with batch calls support + - `list` gets the list of numerators, with batch calls support + - `update` updates an existing numerator, with batch calls support + - `delete` deletes a numerator, with batch calls support + - `get` gets information about the numerator by its identifier + - `count` counts numerators +- Added service `Services\Documentgenerator\Template` with support for `documentgenerator.template.*` methods, + see [documentgenerator.template.* methods](https://apidocs.bitrix24.com/api-reference/document-generator/templates/index.html) ([#489](https://github.com/bitrix24/b24phpsdk/issues/489)): + - `add` creates a new template, with batch calls support + - `list` gets the list of templates, with batch calls support + - `update` updates an existing template, with batch calls support + - `delete` deletes a template, with batch calls support + - `get` gets information about the template by its identifier + - `getFields` returns the description of template fields + - `count` counts templates +- Added service `Services\Documentgenerator\Document` with support for `documentgenerator.document.*` methods, + see [documentgenerator.document.* methods](https://apidocs.bitrix24.com/api-reference/document-generator/index.html) ([#489](https://github.com/bitrix24/b24phpsdk/issues/489)): + - `add` creates a new document based on a template and data provider, with batch calls support + - `list` gets the list of documents, with batch calls support + - `update` updates an existing document, with batch calls support + - `delete` deletes a document, with batch calls support + - `get` gets information about the document by its identifier + - `getFields` returns the description of document fields + - `enablePublicUrl` enables or disables public URL for a document + - `count` counts documents + ### Changed ### Fixed diff --git a/Makefile b/Makefile index 397095ac..688bcd53 100644 --- a/Makefile +++ b/Makefile @@ -635,6 +635,62 @@ integration_tests_crm_documentgenerator_document: integration_tests_crm_documentgenerator_template: docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_crm_documentgenerator_template +.PHONY: integration_tests_scope_documentgenerator +integration_tests_scope_documentgenerator: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_scope_documentgenerator + +.PHONY: integration_tests_documentgenerator_document +integration_tests_documentgenerator_document: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_documentgenerator_document + +.PHONY: integration_tests_documentgenerator_template +integration_tests_documentgenerator_template: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_documentgenerator_template + +.PHONY: integration_tests_documentgenerator_template_service +integration_tests_documentgenerator_template_service: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_documentgenerator_template_service + +.PHONY: integration_tests_documentgenerator_template_annotations +integration_tests_documentgenerator_template_annotations: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_documentgenerator_template_annotations + +.PHONY: integration_tests_documentgenerator_numerator +integration_tests_documentgenerator_numerator: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_documentgenerator_numerator + +.PHONY: integration_tests_documentgenerator_numerator_service +integration_tests_documentgenerator_numerator_service: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_documentgenerator_numerator_service + +.PHONY: integration_tests_documentgenerator_numerator_annotations +integration_tests_documentgenerator_numerator_annotations: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_documentgenerator_numerator_annotations + +.PHONY: integration_tests_documentgenerator_region +integration_tests_documentgenerator_region: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_documentgenerator_region + +.PHONY: integration_tests_documentgenerator_region_service +integration_tests_documentgenerator_region_service: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_documentgenerator_region_service + +.PHONY: integration_tests_documentgenerator_region_annotations +integration_tests_documentgenerator_region_annotations: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_documentgenerator_region_annotations + +.PHONY: integration_tests_documentgenerator_role +integration_tests_documentgenerator_role: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_documentgenerator_role + +.PHONY: integration_tests_documentgenerator_role_service +integration_tests_documentgenerator_role_service: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_documentgenerator_role_service + +.PHONY: integration_tests_documentgenerator_role_annotations +integration_tests_documentgenerator_role_annotations: + docker compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_documentgenerator_role_annotations + # work dev environment .PHONY: php-dev-server-up php-dev-server-up: diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 36a9a3f3..58d242d4 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -38,6 +38,7 @@ parameters: - tests/Integration/Services/CRM/Documentgenerator/Numerator - tests/Integration/Services/CRM/Documentgenerator/Document - tests/Integration/Services/CRM/Documentgenerator/Template + - tests/Integration/Services/Documentgenerator excludePaths: # TODO: Fix type errors in RequisiteUserfieldUseCaseTest and remove this exclusion # Tracking: https://github.com/bitrix24/b24phpsdk/issues diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d431bba8..81ed1f06 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -329,6 +329,48 @@ ./tests/Integration/Services/CRM/Documentgenerator/Template/ + + ./tests/Integration/Services/Documentgenerator/ + + + ./tests/Integration/Services/Documentgenerator/Document/ + + + ./tests/Integration/Services/Documentgenerator/Template/ + + + ./tests/Integration/Services/Documentgenerator/Template/Service/ + + + ./tests/Integration/Services/Documentgenerator/Template/Result/TemplateItemResultAnnotationsTest.php + + + ./tests/Integration/Services/Documentgenerator/Numerator/ + + + ./tests/Integration/Services/Documentgenerator/Numerator/Service/ + + + ./tests/Integration/Services/Documentgenerator/Numerator/Result/NumeratorItemResultAnnotationsTest.php + + + ./tests/Integration/Services/Documentgenerator/Region/ + + + ./tests/Integration/Services/Documentgenerator/Region/Service/ + + + ./tests/Integration/Services/Documentgenerator/Region/Result/RegionItemResultAnnotationsTest.php + + + ./tests/Integration/Services/Documentgenerator/Role/ + + + ./tests/Integration/Services/Documentgenerator/Role/Service/ + + + ./tests/Integration/Services/Documentgenerator/Role/Result/RoleItemResultAnnotationsTest.php + ./tests/Integration/Services/SonetGroup/ diff --git a/rector.php b/rector.php index fab5a568..ac0f61cb 100644 --- a/rector.php +++ b/rector.php @@ -80,6 +80,8 @@ __DIR__ . '/tests/Integration/Services/CRM/Documentgenerator/Document', __DIR__ . '/src/Services/CRM/Documentgenerator/Template', __DIR__ . '/tests/Integration/Services/CRM/Documentgenerator/Template', + __DIR__ . '/src/Services/Documentgenerator', + __DIR__ . '/tests/Integration/Services/Documentgenerator', __DIR__ . '/tests/Unit/', ]) ->withCache(cacheDirectory: __DIR__ . '/var/.cache/rector') diff --git a/src/Services/Documentgenerator/Document/Batch.php b/src/Services/Documentgenerator/Document/Batch.php new file mode 100644 index 00000000..3f903d11 --- /dev/null +++ b/src/Services/Documentgenerator/Document/Batch.php @@ -0,0 +1,251 @@ + + * + * 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\Documentgenerator\Document; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Response\DTO\ResponseData; +use Generator; + +/** + * Class Batch + * + * Overrides base Batch to handle parameter naming differences in documentgenerator.document.* REST methods: + * - delete uses 'id' instead of 'ID' + * - update uses 'values' instead of 'fields' + * - list results are wrapped in 'documents' key and use lowercase 'id' + * + * @package Bitrix24\SDK\Services\Documentgenerator\Document + */ +class Batch extends \Bitrix24\SDK\Core\Batch +{ + /** + * Determines the ID key — lowercase 'id' for document generator + */ + #[\Override] + protected function determineKeyId(string $apiMethod, ?array $additionalParameters): string + { + return 'id'; + } + + /** + * Extracts elements from batch result, unwrapping the 'documents' key + */ + #[\Override] + protected function extractElementsFromBatchResult(ResponseData $responseData, bool $isCrmItemsInBatch): array + { + $resultData = $responseData->getResult(); + + if (array_key_exists('documents', $resultData) && is_array($resultData['documents'])) { + return $resultData['documents']; + } + + return $resultData; + } + + /** + * Returns reference field path including 'documents' wrapper for batch query chaining + */ + #[\Override] + protected function getReferenceFieldPath(string $prevCommandId, int $lastIndex, string $keyId, bool $isCrmItemsInBatch): string + { + return sprintf('$result[%s][documents][%d][%s]', $prevCommandId, $lastIndex, $keyId); + } + + /** + * Get traversable list using lowercase 'id' key and 'documents' result wrapper + * + * Delegates to parent implementation which uses overridden helper methods: + * - determineKeyId() returns 'id' instead of 'ID' + * - extractElementsFromBatchResult() unwraps 'documents' key + * - getReferenceFieldPath() includes 'documents' in batch reference path + * + * @param array $order + * @param array $filter + * @param array $select + * + * @return Generator + * @throws BaseException + * @throws \Bitrix24\SDK\Core\Exceptions\TransportException + */ + #[\Override] + public function getTraversableList( + string $apiMethod, + ?array $order = [], + ?array $filter = [], + ?array $select = [], + ?int $limit = null, + ?array $additionalParameters = null + ): Generator { + yield from parent::getTraversableList($apiMethod, $order, $filter, $select, $limit, $additionalParameters); + } + + /** + * Update entity items with batch call + * + * The documentgenerator.document.update method expects 'values' key + * instead of the standard 'fields' key used by most other REST methods. + * + * Update elements in array with structure: + * element_id => [ + * 'values' => [], // required: document values to update + * 'fields' => [], // optional: field configuration + * 'stampsEnabled' => int // optional: whether to apply stamps (1 = yes, 0 = no) + * ] + * + * @param array> $entityItems + * + * @return Generator|ResponseData[] + * @throws BaseException + */ + #[\Override] + public function updateEntityItems(string $apiMethod, array $entityItems): Generator + { + $this->logger->debug( + 'updateEntityItems.start', + [ + 'apiMethod' => $apiMethod, + 'entityItems' => $entityItems, + ] + ); + + try { + $this->clearCommands(); + + foreach ($entityItems as $entityItemId => $entityItem) { + if (!is_int($entityItemId)) { + throw new InvalidArgumentException( + sprintf( + 'invalid type «%s» of document id «%s», document id must be integer type', + gettype($entityItemId), + $entityItemId + ) + ); + } + + if (!array_key_exists('values', $entityItem)) { + throw new InvalidArgumentException( + sprintf('array key «values» not found in entity item with id %s', $entityItemId) + ); + } + + $cmdArguments = [ + 'id' => $entityItemId, + 'values' => $entityItem['values'], + ]; + + if (array_key_exists('fields', $entityItem)) { + $cmdArguments['fields'] = $entityItem['fields']; + } + + if (array_key_exists('stampsEnabled', $entityItem)) { + $cmdArguments['stampsEnabled'] = $entityItem['stampsEnabled']; + } + + $this->registerCommand($apiMethod, $cmdArguments); + } + + foreach ($this->getTraversable(true) as $cnt => $updatedItemResult) { + yield $cnt => $updatedItemResult; + } + } catch (InvalidArgumentException $exception) { + $errorMessage = sprintf('batch update entity items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + throw $exception; + } catch (\Throwable $exception) { + $errorMessage = sprintf('batch update entity items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + + throw new BaseException($errorMessage, $exception->getCode(), $exception); + } + + $this->logger->debug('updateEntityItems.finish'); + } + + /** + * Delete entity items with batch call + * + * @return Generator|ResponseData[] + * @throws BaseException + */ + #[\Override] + public function deleteEntityItems( + string $apiMethod, + array $entityItemId, + ?array $additionalParameters = null + ): Generator { + $this->logger->debug( + 'deleteEntityItems.start', + [ + 'apiMethod' => $apiMethod, + 'entityItems' => $entityItemId, + 'additionalParameters' => $additionalParameters, + ] + ); + + try { + $this->clearCommands(); + foreach ($entityItemId as $cnt => $code) { + if (!is_int($code)) { + throw new InvalidArgumentException( + sprintf( + 'invalid type «%s» of document 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/Documentgenerator/Document/Result/AddedDocumentBatchResult.php b/src/Services/Documentgenerator/Document/Result/AddedDocumentBatchResult.php new file mode 100644 index 00000000..6189a723 --- /dev/null +++ b/src/Services/Documentgenerator/Document/Result/AddedDocumentBatchResult.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Documentgenerator\Document\Result; + +use Bitrix24\SDK\Core\Result\AddedItemBatchResult; + +/** + * Class AddedDocumentBatchResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Document\Result + */ +class AddedDocumentBatchResult extends AddedItemBatchResult +{ + #[\Override] + public function getId(): int + { + return (int)$this->getResponseData()->getResult()['document']['id']; + } +} diff --git a/src/Services/Documentgenerator/Document/Result/AddedDocumentResult.php b/src/Services/Documentgenerator/Document/Result/AddedDocumentResult.php new file mode 100644 index 00000000..8f28a090 --- /dev/null +++ b/src/Services/Documentgenerator/Document/Result/AddedDocumentResult.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\Documentgenerator\Document\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AddedItemResult; + +/** + * Class AddedDocumentResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Document\Result + */ +class AddedDocumentResult extends AddedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function getId(): int + { + return (int)$this->getCoreResponse()->getResponseData()->getResult()['document']['id']; + } +} diff --git a/src/Services/Documentgenerator/Document/Result/DeletedDocumentBatchResult.php b/src/Services/Documentgenerator/Document/Result/DeletedDocumentBatchResult.php new file mode 100644 index 00000000..71c21e85 --- /dev/null +++ b/src/Services/Documentgenerator/Document/Result/DeletedDocumentBatchResult.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Documentgenerator\Document\Result; + +use Bitrix24\SDK\Core\Result\DeletedItemBatchResult; + +/** + * Class DeletedDocumentBatchResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Document\Result + */ +class DeletedDocumentBatchResult extends DeletedItemBatchResult +{ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Document/Result/DeletedDocumentResult.php b/src/Services/Documentgenerator/Document/Result/DeletedDocumentResult.php new file mode 100644 index 00000000..0c1d6f14 --- /dev/null +++ b/src/Services/Documentgenerator/Document/Result/DeletedDocumentResult.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\Documentgenerator\Document\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\DeletedItemResult; + +/** + * Class DeletedDocumentResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Document\Result + */ +class DeletedDocumentResult extends DeletedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getCoreResponse()->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Document/Result/DocumentFieldsResult.php b/src/Services/Documentgenerator/Document/Result/DocumentFieldsResult.php new file mode 100644 index 00000000..9b2d68af --- /dev/null +++ b/src/Services/Documentgenerator/Document/Result/DocumentFieldsResult.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\Documentgenerator\Document\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class DocumentFieldsResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Document\Result + */ +class DocumentFieldsResult extends AbstractResult +{ + /** + * @throws BaseException + */ + public function getFieldsDescription(): array + { + $result = $this->getCoreResponse()->getResponseData()->getResult(); + + // API returns fields nested under documentFields key + if (!empty($result['documentFields']) && is_array($result['documentFields'])) { + return $result['documentFields']; + } + + return $result; + } +} diff --git a/src/Services/Documentgenerator/Document/Result/DocumentItemResult.php b/src/Services/Documentgenerator/Document/Result/DocumentItemResult.php new file mode 100644 index 00000000..7bbd8d32 --- /dev/null +++ b/src/Services/Documentgenerator/Document/Result/DocumentItemResult.php @@ -0,0 +1,62 @@ + + * + * 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\Documentgenerator\Document\Result; + +use Bitrix24\SDK\Core\Result\AbstractAnnotatedItem; +use Carbon\CarbonImmutable; + +/** + * Class DocumentItemResult + * + * @property-read int $id + * @property-read string $title + * @property-read string $number + * @property-read int $templateId + * @property-read string $provider + * @property-read string $value + * @property-read int|null $fileId + * @property-read int|null $imageId + * @property-read int|null $pdfId + * @property-read CarbonImmutable|null $createTime + * @property-read CarbonImmutable|null $updateTime + * @property-read array|null $values + * @property-read int|null $createdBy + * @property-read int|null $updatedBy + * @property-read string|null $downloadUrl + * @property-read string|null $pdfUrl + * @property-read string|null $imageUrl + * @property-read bool|null $stampsEnabled + * @property-read string|null $downloadUrlMachine + * @property-read string|null $pdfUrlMachine + * @property-read string|null $imageUrlMachine + * @property-read string|null $creationMethod + */ +class DocumentItemResult extends AbstractAnnotatedItem +{ + /** + * @param int|string $offset + * + * @return mixed + */ + #[\Override] + public function __get($offset) + { + if ($offset === 'creationMethod') { + // The API field name is '_creationMethod' (with leading underscore) + return $this->data['_creationMethod'] ?? null; + } + + return parent::__get($offset); + } +} diff --git a/src/Services/Documentgenerator/Document/Result/DocumentResult.php b/src/Services/Documentgenerator/Document/Result/DocumentResult.php new file mode 100644 index 00000000..3ac784f7 --- /dev/null +++ b/src/Services/Documentgenerator/Document/Result/DocumentResult.php @@ -0,0 +1,38 @@ + + * + * 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\Documentgenerator\Document\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class DocumentResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Document\Result + */ +class DocumentResult extends AbstractResult +{ + /** + * @throws BaseException + */ + public function document(): DocumentItemResult + { + $result = $this->getCoreResponse()->getResponseData()->getResult(); + if (!empty($result['document']) && is_array($result['document'])) { + $result = $result['document']; + } + + return new DocumentItemResult($result); + } +} diff --git a/src/Services/Documentgenerator/Document/Result/DocumentsResult.php b/src/Services/Documentgenerator/Document/Result/DocumentsResult.php new file mode 100644 index 00000000..7c92e90a --- /dev/null +++ b/src/Services/Documentgenerator/Document/Result/DocumentsResult.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\Documentgenerator\Document\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class DocumentsResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Document\Result + */ +class DocumentsResult extends AbstractResult +{ + /** + * @return DocumentItemResult[] + * @throws BaseException + */ + public function getDocuments(): array + { + $items = []; + $source = []; + + $result = $this->getCoreResponse()->getResponseData()->getResult(); + + if (!empty($result['documents']) && is_array($result['documents'])) { + $source = $result['documents']; + } elseif (!empty($result['items']) && is_array($result['items'])) { + $source = $result['items']; + } + + foreach ($source as $item) { + $items[] = new DocumentItemResult($item); + } + + return $items; + } +} diff --git a/src/Services/Documentgenerator/Document/Result/PublicUrlResult.php b/src/Services/Documentgenerator/Document/Result/PublicUrlResult.php new file mode 100644 index 00000000..21d26f82 --- /dev/null +++ b/src/Services/Documentgenerator/Document/Result/PublicUrlResult.php @@ -0,0 +1,47 @@ + + * + * 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\Documentgenerator\Document\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class PublicUrlResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Document\Result + */ +class PublicUrlResult extends AbstractResult +{ + /** + * @throws BaseException + */ + public function getPublicUrl(): ?string + { + $result = $this->getCoreResponse()->getResponseData()->getResult(); + + if (!empty($result['publicUrl'])) { + return (string)$result['publicUrl']; + } + + return null; + } + + /** + * @throws BaseException + */ + public function isSuccess(): bool + { + return (bool)$this->getCoreResponse()->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Document/Result/UpdatedDocumentBatchResult.php b/src/Services/Documentgenerator/Document/Result/UpdatedDocumentBatchResult.php new file mode 100644 index 00000000..5bb4b6e5 --- /dev/null +++ b/src/Services/Documentgenerator/Document/Result/UpdatedDocumentBatchResult.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Documentgenerator\Document\Result; + +use Bitrix24\SDK\Core\Result\UpdatedItemBatchResult; + +/** + * Class UpdatedDocumentBatchResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Document\Result + */ +class UpdatedDocumentBatchResult extends UpdatedItemBatchResult +{ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Document/Result/UpdatedDocumentResult.php b/src/Services/Documentgenerator/Document/Result/UpdatedDocumentResult.php new file mode 100644 index 00000000..cbfc8e18 --- /dev/null +++ b/src/Services/Documentgenerator/Document/Result/UpdatedDocumentResult.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\Documentgenerator\Document\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\UpdatedItemResult; + +/** + * Class UpdatedDocumentResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Document\Result + */ +class UpdatedDocumentResult extends UpdatedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getCoreResponse()->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Document/Service/Batch.php b/src/Services/Documentgenerator/Document/Service/Batch.php new file mode 100644 index 00000000..273f85f2 --- /dev/null +++ b/src/Services/Documentgenerator/Document/Service/Batch.php @@ -0,0 +1,153 @@ + + * + * 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\Documentgenerator\Document\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\Documentgenerator\Document\Result\AddedDocumentBatchResult; +use Bitrix24\SDK\Services\Documentgenerator\Document\Result\DeletedDocumentBatchResult; +use Bitrix24\SDK\Services\Documentgenerator\Document\Result\DocumentItemResult; +use Bitrix24\SDK\Services\Documentgenerator\Document\Result\UpdatedDocumentBatchResult; +use Generator; +use Psr\Log\LoggerInterface; + +#[ApiBatchServiceMetadata(new Scope(['documentgenerator']))] +class Batch +{ + /** + * Batch constructor + */ + public function __construct(protected BatchOperationsInterface $batch, protected LoggerInterface $log) + { + } + + /** + * Batch list method for documents + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'documentgenerator.document.list', + 'https://apidocs.bitrix24.com/api-reference/document-generator/document-generator-document-list.html', + 'Batch list method for documents' + )] + public function list(?int $limit = null): Generator + { + $this->log->debug( + 'batchList', + [ + 'limit' => $limit, + ] + ); + + $documentListGenerator = $this->batch->getTraversableListWithCount( + 'documentgenerator.document.list', + [], + [], + [], + $limit + ); + foreach ($documentListGenerator as $key => $value) { + yield $key => new DocumentItemResult($value); + } + } + + /** + * Batch adding documents + * + * @param array $documents + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'documentgenerator.document.add', + 'https://apidocs.bitrix24.com/api-reference/document-generator/document-generator-document-add.html', + 'Batch adding documents' + )] + public function add(array $documents): Generator + { + foreach ($this->batch->addEntityItems('documentgenerator.document.add', $documents) as $key => $item) { + yield $key => new AddedDocumentBatchResult($item); + } + } + + /** + * Batch update documents + * + * Update elements in array with structure: + * id => [ // Document id + * 'values' => [], // Document values to update + * 'fields' => [], // Optional: field configuration + * 'stampsEnabled' => int // Optional: whether to apply stamps (1 = yes, 0 = no) + * ] + * + * @param array $entityItems + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'documentgenerator.document.update', + 'https://apidocs.bitrix24.com/api-reference/document-generator/document-generator-document-update.html', + 'Update in batch mode a list of documents' + )] + public function update(array $entityItems): Generator + { + foreach ( + $this->batch->updateEntityItems( + 'documentgenerator.document.update', + $entityItems + ) as $key => $item + ) { + yield $key => new UpdatedDocumentBatchResult($item); + } + } + + /** + * Batch delete documents + * + * @param int[] $documentId + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'documentgenerator.document.delete', + 'https://apidocs.bitrix24.com/api-reference/document-generator/document-generator-document-delete.html', + 'Batch delete documents' + )] + public function delete(array $documentId): Generator + { + foreach ( + $this->batch->deleteEntityItems( + 'documentgenerator.document.delete', + $documentId + ) as $key => $item + ) { + yield $key => new DeletedDocumentBatchResult($item); + } + } +} diff --git a/src/Services/Documentgenerator/Document/Service/Document.php b/src/Services/Documentgenerator/Document/Service/Document.php new file mode 100644 index 00000000..b4d17937 --- /dev/null +++ b/src/Services/Documentgenerator/Document/Service/Document.php @@ -0,0 +1,299 @@ + + * + * 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\Documentgenerator\Document\Service; + +use Bitrix24\SDK\Attributes\ApiEndpointMetadata; +use Bitrix24\SDK\Attributes\ApiServiceMetadata; +use Bitrix24\SDK\Core\Contracts\CoreInterface; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\Documentgenerator\Document\Result\AddedDocumentResult; +use Bitrix24\SDK\Services\Documentgenerator\Document\Result\DeletedDocumentResult; +use Bitrix24\SDK\Services\Documentgenerator\Document\Result\DocumentFieldsResult; +use Bitrix24\SDK\Services\Documentgenerator\Document\Result\DocumentResult; +use Bitrix24\SDK\Services\Documentgenerator\Document\Result\DocumentsResult; +use Bitrix24\SDK\Services\Documentgenerator\Document\Result\PublicUrlResult; +use Bitrix24\SDK\Services\Documentgenerator\Document\Result\UpdatedDocumentResult; +use Psr\Log\LoggerInterface; + +#[ApiServiceMetadata(new Scope(['documentgenerator']))] +class Document extends AbstractService +{ + /** + * Document constructor + */ + public function __construct(public Batch $batch, CoreInterface $core, LoggerInterface $logger) + { + parent::__construct($core, $logger); + } + + /** + * Creates a new document based on a template and data provider + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/document-generator-document-add.html + * + * @param int $templateId Template identifier + * @param string $providerClassName Data provider class name (e.g. 'Bitrix\DocumentGenerator\DataProvider\Rest') + * @param string $value External identifier of the data source (e.g. 'ORDER_1024') + * @param array $values Field values for the document + * @param array $fields Field configuration (providers, types, etc.) + * @param int|null $stampsEnabled Whether to apply stamps (1 = yes, 0 = no) + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.document.add', + 'https://apidocs.bitrix24.com/api-reference/document-generator/document-generator-document-add.html', + 'Creates a new document based on a template and data provider' + )] + public function add( + int $templateId, + string $providerClassName, + string $value, + array $values = [], + array $fields = [], + ?int $stampsEnabled = null + ): AddedDocumentResult { + $params = [ + 'templateId' => $templateId, + 'providerClassName' => $providerClassName, + 'value' => $value, + ]; + + if ($values !== []) { + $params['values'] = $values; + } + + if ($fields !== []) { + $params['fields'] = $fields; + } + + if ($stampsEnabled !== null) { + $params['stampsEnabled'] = $stampsEnabled; + } + + return new AddedDocumentResult( + $this->core->call( + 'documentgenerator.document.add', + $params + ) + ); + } + + /** + * Updates an existing document + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/document-generator-document-update.html + * + * @param int $id Document identifier + * @param array $values Field values to update + * @param array $fields Field configuration + * @param int|null $stampsEnabled Whether to apply stamps (1 = yes, 0 = no) + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.document.update', + 'https://apidocs.bitrix24.com/api-reference/document-generator/document-generator-document-update.html', + 'Updates an existing document' + )] + public function update(int $id, array $values = [], array $fields = [], ?int $stampsEnabled = null): UpdatedDocumentResult + { + $params = [ + 'id' => $id, + ]; + + if ($values !== []) { + $params['values'] = $values; + } + + if ($fields !== []) { + $params['fields'] = $fields; + } + + if ($stampsEnabled !== null) { + $params['stampsEnabled'] = $stampsEnabled; + } + + return new UpdatedDocumentResult( + $this->core->call( + 'documentgenerator.document.update', + $params + ) + ); + } + + /** + * Returns information about the document by its identifier + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/document-generator-document-get.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.document.get', + 'https://apidocs.bitrix24.com/api-reference/document-generator/document-generator-document-get.html', + 'Returns information about the document by its identifier' + )] + public function get(int $id): DocumentResult + { + return new DocumentResult($this->core->call('documentgenerator.document.get', ['id' => $id])); + } + + /** + * Returns a list of documents + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/document-generator-document-list.html + * + * @param array $filter Filter parameters + * @param array $order Order parameters + * @param array $select Fields to select + * @param int $start Offset for pagination + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.document.list', + 'https://apidocs.bitrix24.com/api-reference/document-generator/document-generator-document-list.html', + 'Returns a list of documents' + )] + public function list(array $filter = [], array $order = [], array $select = [], int $start = 0): DocumentsResult + { + $params = [ + 'start' => $start, + ]; + + if ($filter !== []) { + $params['filter'] = $filter; + } + + if ($order !== []) { + $params['order'] = $order; + } + + if ($select !== []) { + $params['select'] = $select; + } + + return new DocumentsResult( + $this->core->call( + 'documentgenerator.document.list', + $params + ) + ); + } + + /** + * Deletes a document + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/document-generator-document-delete.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.document.delete', + 'https://apidocs.bitrix24.com/api-reference/document-generator/document-generator-document-delete.html', + 'Deletes a document' + )] + public function delete(int $id): DeletedDocumentResult + { + return new DeletedDocumentResult( + $this->core->call( + 'documentgenerator.document.delete', + ['id' => $id] + ) + ); + } + + /** + * Enables or disables public URL for a document + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/document-generator-document-enable-public-url.html + * + * @param int $id Document identifier + * @param int $status 1 to enable public URL, 0 to disable (default: 1) + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.document.enablepublicurl', + 'https://apidocs.bitrix24.com/api-reference/document-generator/document-generator-document-enable-public-url.html', + 'Enables or disables public URL for a document' + )] + public function enablePublicUrl(int $id, int $status = 1): PublicUrlResult + { + return new PublicUrlResult( + $this->core->call( + 'documentgenerator.document.enablepublicurl', + [ + 'id' => $id, + 'status' => $status, + ] + ) + ); + } + + /** + * Returns the description of document fields + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/document-generator-document-get-fields.html + * + * @param int $id Document identifier + * @param array $values Optional field values + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.document.getfields', + 'https://apidocs.bitrix24.com/api-reference/document-generator/document-generator-document-get-fields.html', + 'Returns the description of document fields' + )] + public function getFields(int $id, array $values = []): DocumentFieldsResult + { + $params = [ + 'id' => $id, + ]; + + if ($values !== []) { + $params['values'] = $values; + } + + return new DocumentFieldsResult( + $this->core->call( + 'documentgenerator.document.getfields', + $params + ) + ); + } + + /** + * Count documents + * + * @throws BaseException + * @throws TransportException + */ + public function count(): int + { + return $this->list()->getCoreResponse()->getResponseData()->getPagination()->getTotal(); + } +} diff --git a/src/Services/Documentgenerator/DocumentgeneratorServiceBuilder.php b/src/Services/Documentgenerator/DocumentgeneratorServiceBuilder.php new file mode 100644 index 00000000..ed4526a4 --- /dev/null +++ b/src/Services/Documentgenerator/DocumentgeneratorServiceBuilder.php @@ -0,0 +1,112 @@ + + * + * 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\Documentgenerator; + +use Bitrix24\SDK\Attributes\ApiServiceBuilderMetadata; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Services\AbstractServiceBuilder; +use Bitrix24\SDK\Services\Documentgenerator\Document; +use Bitrix24\SDK\Services\Documentgenerator\Numerator; +use Bitrix24\SDK\Services\Documentgenerator\Region; +use Bitrix24\SDK\Services\Documentgenerator\Role; +use Bitrix24\SDK\Services\Documentgenerator\Template; + +#[ApiServiceBuilderMetadata(new Scope(['documentgenerator']))] +class DocumentgeneratorServiceBuilder extends AbstractServiceBuilder +{ + public function document(): Document\Service\Document + { + if (!isset($this->serviceCache[__METHOD__])) { + $documentBatch = new Document\Batch( + $this->core, + $this->log + ); + $this->serviceCache[__METHOD__] = new Document\Service\Document( + new Document\Service\Batch($documentBatch, $this->log), + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } + + public function template(): Template\Service\Template + { + if (!isset($this->serviceCache[__METHOD__])) { + $templateBatch = new Template\Batch( + $this->core, + $this->log + ); + $this->serviceCache[__METHOD__] = new Template\Service\Template( + new Template\Service\Batch($templateBatch, $this->log), + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } + + public function numerator(): Numerator\Service\Numerator + { + if (!isset($this->serviceCache[__METHOD__])) { + $numeratorBatch = new Numerator\Batch( + $this->core, + $this->log + ); + $this->serviceCache[__METHOD__] = new Numerator\Service\Numerator( + new Numerator\Service\Batch($numeratorBatch, $this->log), + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } + + public function region(): Region\Service\Region + { + if (!isset($this->serviceCache[__METHOD__])) { + $regionBatch = new Region\Batch( + $this->core, + $this->log + ); + $this->serviceCache[__METHOD__] = new Region\Service\Region( + new Region\Service\Batch($regionBatch, $this->log), + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } + + public function role(): Role\Service\Role + { + if (!isset($this->serviceCache[__METHOD__])) { + $roleBatch = new Role\Batch( + $this->core, + $this->log + ); + $this->serviceCache[__METHOD__] = new Role\Service\Role( + new Role\Service\Batch($roleBatch, $this->log), + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } +} diff --git a/src/Services/Documentgenerator/Numerator/Batch.php b/src/Services/Documentgenerator/Numerator/Batch.php new file mode 100644 index 00000000..451b2acd --- /dev/null +++ b/src/Services/Documentgenerator/Numerator/Batch.php @@ -0,0 +1,241 @@ + + * + * 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\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 + * + * Overrides base Batch to handle parameter naming differences in documentgenerator.numerator.* REST methods: + * - delete uses 'id' instead of 'ID' + * - update uses 'id' instead of 'ID' + * - list results are wrapped in 'numerators' key and use lowercase 'id' + * + * @package Bitrix24\SDK\Services\Documentgenerator\Numerator + */ +class Batch extends \Bitrix24\SDK\Core\Batch +{ + /** + * Determines the ID key — lowercase 'id' for document generator numerator + */ + #[\Override] + protected function determineKeyId(string $apiMethod, ?array $additionalParameters): string + { + return 'id'; + } + + /** + * Extracts elements from batch result, unwrapping the 'numerators' key + */ + #[\Override] + protected function extractElementsFromBatchResult(ResponseData $responseData, bool $isCrmItemsInBatch): array + { + $resultData = $responseData->getResult(); + + if (array_key_exists('numerators', $resultData) && is_array($resultData['numerators'])) { + return $resultData['numerators']; + } + + return $resultData; + } + + /** + * Returns reference field path including 'numerators' wrapper for batch query chaining + */ + #[\Override] + protected function getReferenceFieldPath(string $prevCommandId, int $lastIndex, string $keyId, bool $isCrmItemsInBatch): string + { + return sprintf('$result[%s][numerators][%d][%s]', $prevCommandId, $lastIndex, $keyId); + } + + /** + * Get traversable list using lowercase 'id' key and 'numerators' result wrapper + * + * Delegates to parent implementation which uses overridden helper methods: + * - determineKeyId() returns 'id' instead of 'ID' + * - extractElementsFromBatchResult() unwraps 'numerators' key + * - getReferenceFieldPath() includes 'numerators' in batch reference path + * + * @param array $order + * @param array $filter + * @param array $select + * + * @return Generator + * @throws BaseException + * @throws \Bitrix24\SDK\Core\Exceptions\TransportException + */ + #[\Override] + public function getTraversableList( + string $apiMethod, + ?array $order = [], + ?array $filter = [], + ?array $select = [], + ?int $limit = null, + ?array $additionalParameters = null + ): Generator { + yield from parent::getTraversableList($apiMethod, $order, $filter, $select, $limit, $additionalParameters); + } + + /** + * Update entity items with batch call + * + * The documentgenerator.numerator.update method uses 'id' (lowercase) + * instead of the standard 'ID' key used by most other REST methods. + * + * Update elements in array with structure: + * element_id => [ + * 'fields' => [] // required: numerator fields to update + * ] + * + * @param array> $entityItems + * + * @return Generator|ResponseData[] + * @throws BaseException + */ + #[\Override] + public function updateEntityItems(string $apiMethod, array $entityItems): Generator + { + $this->logger->debug( + 'updateEntityItems.start', + [ + 'apiMethod' => $apiMethod, + 'entityItems' => $entityItems, + ] + ); + + try { + $this->clearCommands(); + + foreach ($entityItems as $entityItemId => $entityItem) { + if (!is_int($entityItemId)) { + throw new InvalidArgumentException( + sprintf( + 'invalid type «%s» of numerator id «%s», the id must be integer type', + gettype($entityItemId), + $entityItemId + ) + ); + } + + if (!array_key_exists('fields', $entityItem)) { + throw new InvalidArgumentException( + sprintf('array key «fields» not found in entity item with id %s', $entityItemId) + ); + } + + $cmdArguments = [ + 'id' => $entityItemId, + 'fields' => $entityItem['fields'], + ]; + + $this->registerCommand($apiMethod, $cmdArguments); + } + + foreach ($this->getTraversable(true) as $cnt => $updatedItemResult) { + yield $cnt => $updatedItemResult; + } + } catch (InvalidArgumentException $exception) { + $errorMessage = sprintf('batch update entity items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + throw $exception; + } catch (\Throwable $exception) { + $errorMessage = sprintf('batch update entity items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + + throw new BaseException($errorMessage, $exception->getCode(), $exception); + } + + $this->logger->debug('updateEntityItems.finish'); + } + + /** + * Delete entity items with batch call + * + * @return Generator|ResponseData[] + * @throws BaseException + */ + #[\Override] + public function deleteEntityItems( + string $apiMethod, + array $entityItemId, + ?array $additionalParameters = null + ): Generator { + $this->logger->debug( + 'deleteEntityItems.start', + [ + 'apiMethod' => $apiMethod, + 'entityItems' => $entityItemId, + 'additionalParameters' => $additionalParameters, + ] + ); + + try { + $this->clearCommands(); + foreach ($entityItemId as $cnt => $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/Documentgenerator/Numerator/Result/AddedNumeratorBatchResult.php b/src/Services/Documentgenerator/Numerator/Result/AddedNumeratorBatchResult.php new file mode 100644 index 00000000..2b45d885 --- /dev/null +++ b/src/Services/Documentgenerator/Numerator/Result/AddedNumeratorBatchResult.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Documentgenerator\Numerator\Result; + +use Bitrix24\SDK\Core\Result\AddedItemBatchResult; + +/** + * Class AddedNumeratorBatchResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Numerator\Result + */ +class AddedNumeratorBatchResult extends AddedItemBatchResult +{ + #[\Override] + public function getId(): int + { + return (int)$this->getResponseData()->getResult()['numerator']['id']; + } +} diff --git a/src/Services/Documentgenerator/Numerator/Result/AddedNumeratorResult.php b/src/Services/Documentgenerator/Numerator/Result/AddedNumeratorResult.php new file mode 100644 index 00000000..63023dd1 --- /dev/null +++ b/src/Services/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\Documentgenerator\Numerator\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AddedItemResult; + +/** + * Class AddedNumeratorResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Numerator\Result + */ +class AddedNumeratorResult extends AddedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function getId(): int + { + return (int)$this->getCoreResponse()->getResponseData()->getResult()['numerator']['id']; + } +} diff --git a/src/Services/Documentgenerator/Numerator/Result/DeletedNumeratorBatchResult.php b/src/Services/Documentgenerator/Numerator/Result/DeletedNumeratorBatchResult.php new file mode 100644 index 00000000..8446502b --- /dev/null +++ b/src/Services/Documentgenerator/Numerator/Result/DeletedNumeratorBatchResult.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Documentgenerator\Numerator\Result; + +use Bitrix24\SDK\Core\Result\DeletedItemBatchResult; + +/** + * Class DeletedNumeratorBatchResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Numerator\Result + */ +class DeletedNumeratorBatchResult extends DeletedItemBatchResult +{ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Numerator/Result/DeletedNumeratorResult.php b/src/Services/Documentgenerator/Numerator/Result/DeletedNumeratorResult.php new file mode 100644 index 00000000..7c4340fd --- /dev/null +++ b/src/Services/Documentgenerator/Numerator/Result/DeletedNumeratorResult.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\Documentgenerator\Numerator\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\DeletedItemResult; + +/** + * Class DeletedNumeratorResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Numerator\Result + */ +class DeletedNumeratorResult extends DeletedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getCoreResponse()->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Numerator/Result/NumeratorItemResult.php b/src/Services/Documentgenerator/Numerator/Result/NumeratorItemResult.php new file mode 100644 index 00000000..06113340 --- /dev/null +++ b/src/Services/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\Documentgenerator\Numerator\Result; + +use Bitrix24\SDK\Core\Result\AbstractAnnotatedItem; + +/** + * Class NumeratorItemResult + * + * @property-read int $id + * @property-read string $name + * @property-read string $template + * @property-read array|null $settings + */ +class NumeratorItemResult extends AbstractAnnotatedItem +{ +} diff --git a/src/Services/Documentgenerator/Numerator/Result/NumeratorResult.php b/src/Services/Documentgenerator/Numerator/Result/NumeratorResult.php new file mode 100644 index 00000000..b2c1828b --- /dev/null +++ b/src/Services/Documentgenerator/Numerator/Result/NumeratorResult.php @@ -0,0 +1,38 @@ + + * + * 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\Documentgenerator\Numerator\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class NumeratorResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Numerator\Result + */ +class NumeratorResult extends AbstractResult +{ + /** + * @throws BaseException + */ + public function numerator(): NumeratorItemResult + { + $result = $this->getCoreResponse()->getResponseData()->getResult(); + if (!empty($result['numerator']) && is_array($result['numerator'])) { + $result = $result['numerator']; + } + + return new NumeratorItemResult($result); + } +} diff --git a/src/Services/Documentgenerator/Numerator/Result/NumeratorsResult.php b/src/Services/Documentgenerator/Numerator/Result/NumeratorsResult.php new file mode 100644 index 00000000..8fbc538f --- /dev/null +++ b/src/Services/Documentgenerator/Numerator/Result/NumeratorsResult.php @@ -0,0 +1,47 @@ + + * + * 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\Documentgenerator\Numerator\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class NumeratorsResult + * + * @package Bitrix24\SDK\Services\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']; + } + + foreach ($source as $item) { + $items[] = new NumeratorItemResult($item); + } + + return $items; + } +} diff --git a/src/Services/Documentgenerator/Numerator/Result/UpdatedNumeratorBatchResult.php b/src/Services/Documentgenerator/Numerator/Result/UpdatedNumeratorBatchResult.php new file mode 100644 index 00000000..c687a3a4 --- /dev/null +++ b/src/Services/Documentgenerator/Numerator/Result/UpdatedNumeratorBatchResult.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Documentgenerator\Numerator\Result; + +use Bitrix24\SDK\Core\Result\UpdatedItemBatchResult; + +/** + * Class UpdatedNumeratorBatchResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Numerator\Result + */ +class UpdatedNumeratorBatchResult extends UpdatedItemBatchResult +{ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Numerator/Result/UpdatedNumeratorResult.php b/src/Services/Documentgenerator/Numerator/Result/UpdatedNumeratorResult.php new file mode 100644 index 00000000..a78eb137 --- /dev/null +++ b/src/Services/Documentgenerator/Numerator/Result/UpdatedNumeratorResult.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\Documentgenerator\Numerator\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\UpdatedItemResult; + +/** + * Class UpdatedNumeratorResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Numerator\Result + */ +class UpdatedNumeratorResult extends UpdatedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getCoreResponse()->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Numerator/Service/Batch.php b/src/Services/Documentgenerator/Numerator/Service/Batch.php new file mode 100644 index 00000000..5d01ac1f --- /dev/null +++ b/src/Services/Documentgenerator/Numerator/Service/Batch.php @@ -0,0 +1,157 @@ + + * + * 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\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\Documentgenerator\Numerator\Result\AddedNumeratorBatchResult; +use Bitrix24\SDK\Services\Documentgenerator\Numerator\Result\DeletedNumeratorBatchResult; +use Bitrix24\SDK\Services\Documentgenerator\Numerator\Result\NumeratorItemResult; +use Bitrix24\SDK\Services\Documentgenerator\Numerator\Result\UpdatedNumeratorBatchResult; +use Generator; +use Psr\Log\LoggerInterface; + +#[ApiBatchServiceMetadata(new Scope(['documentgenerator']))] +class Batch +{ + /** + * Batch constructor + */ + public function __construct(protected BatchOperationsInterface $batch, protected LoggerInterface $log) + { + } + + /** + * Batch list method for numerators + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/numerators/document-generator-numerator-list.html + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'documentgenerator.numerator.list', + 'https://apidocs.bitrix24.com/api-reference/document-generator/numerators/document-generator-numerator-list.html', + 'Batch list method for numerators' + )] + public function list(?int $limit = null): Generator + { + $this->log->debug( + 'batchList', + [ + 'limit' => $limit, + ] + ); + + $numeratorListGenerator = $this->batch->getTraversableListWithCount( + '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( + 'documentgenerator.numerator.add', + 'https://apidocs.bitrix24.com/api-reference/document-generator/numerators/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('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( + 'documentgenerator.numerator.update', + 'https://apidocs.bitrix24.com/api-reference/document-generator/numerators/document-generator-numerator-update.html', + 'Update in batch mode a list of numerators' + )] + public function update(array $entityItems): Generator + { + foreach ( + $this->batch->updateEntityItems( + 'documentgenerator.numerator.update', + $entityItems + ) as $key => $item + ) { + yield $key => new UpdatedNumeratorBatchResult($item); + } + } + + /** + * Batch delete numerators + * + * @param int[] $numeratorId + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'documentgenerator.numerator.delete', + 'https://apidocs.bitrix24.com/api-reference/document-generator/numerators/document-generator-numerator-delete.html', + 'Batch delete numerators' + )] + public function delete(array $numeratorId): Generator + { + foreach ( + $this->batch->deleteEntityItems( + 'documentgenerator.numerator.delete', + $numeratorId + ) as $key => $item + ) { + yield $key => new DeletedNumeratorBatchResult($item); + } + } +} diff --git a/src/Services/Documentgenerator/Numerator/Service/Numerator.php b/src/Services/Documentgenerator/Numerator/Service/Numerator.php new file mode 100644 index 00000000..07886a13 --- /dev/null +++ b/src/Services/Documentgenerator/Numerator/Service/Numerator.php @@ -0,0 +1,184 @@ + + * + * 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\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\Documentgenerator\Numerator\Result\AddedNumeratorResult; +use Bitrix24\SDK\Services\Documentgenerator\Numerator\Result\DeletedNumeratorResult; +use Bitrix24\SDK\Services\Documentgenerator\Numerator\Result\NumeratorResult; +use Bitrix24\SDK\Services\Documentgenerator\Numerator\Result\NumeratorsResult; +use Bitrix24\SDK\Services\Documentgenerator\Numerator\Result\UpdatedNumeratorResult; +use Psr\Log\LoggerInterface; + +#[ApiServiceMetadata(new Scope(['documentgenerator']))] +class Numerator extends AbstractService +{ + /** + * Numerator constructor + */ + public function __construct(public Batch $batch, CoreInterface $core, LoggerInterface $logger) + { + parent::__construct($core, $logger); + } + + /** + * Creates a new numerator + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/numerators/document-generator-numerator-add.html + * + * @param array{ + * name: string, + * template: string, + * settings?: array + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.numerator.add', + 'https://apidocs.bitrix24.com/api-reference/document-generator/numerators/document-generator-numerator-add.html', + 'Creates a new numerator' + )] + public function add(array $fields): AddedNumeratorResult + { + return new AddedNumeratorResult( + $this->core->call( + 'documentgenerator.numerator.add', + [ + 'fields' => $fields, + ] + ) + ); + } + + /** + * Updates an existing numerator with new values + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/numerators/document-generator-numerator-update.html + * + * @param array{ + * name?: string, + * template?: string, + * settings?: array + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.numerator.update', + 'https://apidocs.bitrix24.com/api-reference/document-generator/numerators/document-generator-numerator-update.html', + 'Updates an existing numerator with new values' + )] + public function update(int $id, array $fields): UpdatedNumeratorResult + { + return new UpdatedNumeratorResult( + $this->core->call( + 'documentgenerator.numerator.update', + [ + 'id' => $id, + 'fields' => $fields, + ] + ) + ); + } + + /** + * Returns information about the numerator by its identifier + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/numerators/document-generator-numerator-get.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.numerator.get', + 'https://apidocs.bitrix24.com/api-reference/document-generator/numerators/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('documentgenerator.numerator.get', ['id' => $id]) + ); + } + + /** + * Returns a list of numerators + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/numerators/document-generator-numerator-list.html + * + * @param int $start Offset for pagination + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.numerator.list', + 'https://apidocs.bitrix24.com/api-reference/document-generator/numerators/document-generator-numerator-list.html', + 'Returns a list of numerators' + )] + public function list(int $start = 0): NumeratorsResult + { + return new NumeratorsResult( + $this->core->call( + 'documentgenerator.numerator.list', + [ + 'start' => $start, + ] + ) + ); + } + + /** + * Deletes a numerator + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/numerators/document-generator-numerator-delete.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.numerator.delete', + 'https://apidocs.bitrix24.com/api-reference/document-generator/numerators/document-generator-numerator-delete.html', + 'Deletes a numerator' + )] + public function delete(int $id): DeletedNumeratorResult + { + return new DeletedNumeratorResult( + $this->core->call( + 'documentgenerator.numerator.delete', + ['id' => $id] + ) + ); + } + + /** + * Count numerators + * + * @throws BaseException + * @throws TransportException + */ + public function count(): int + { + return $this->list()->getCoreResponse()->getResponseData()->getPagination()->getTotal(); + } +} diff --git a/src/Services/Documentgenerator/Region/Batch.php b/src/Services/Documentgenerator/Region/Batch.php new file mode 100644 index 00000000..69bb993a --- /dev/null +++ b/src/Services/Documentgenerator/Region/Batch.php @@ -0,0 +1,241 @@ + + * + * 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\Documentgenerator\Region; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Response\DTO\ResponseData; +use Generator; + +/** + * Class Batch + * + * Overrides base Batch to handle parameter naming differences in documentgenerator.region.* REST methods: + * - delete uses 'id' instead of 'ID' + * - update uses 'id' instead of 'ID' + * - list results are wrapped in 'regions' key and use lowercase 'id' + * + * @package Bitrix24\SDK\Services\Documentgenerator\Region + */ +class Batch extends \Bitrix24\SDK\Core\Batch +{ + /** + * Determines the ID key — lowercase 'id' for document generator region + */ + #[\Override] + protected function determineKeyId(string $apiMethod, ?array $additionalParameters): string + { + return 'id'; + } + + /** + * Extracts elements from batch result, unwrapping the 'regions' key + */ + #[\Override] + protected function extractElementsFromBatchResult(ResponseData $responseData, bool $isCrmItemsInBatch): array + { + $resultData = $responseData->getResult(); + + if (array_key_exists('regions', $resultData) && is_array($resultData['regions'])) { + return $resultData['regions']; + } + + return $resultData; + } + + /** + * Returns reference field path including 'regions' wrapper for batch query chaining + */ + #[\Override] + protected function getReferenceFieldPath(string $prevCommandId, int $lastIndex, string $keyId, bool $isCrmItemsInBatch): string + { + return sprintf('$result[%s][regions][%d][%s]', $prevCommandId, $lastIndex, $keyId); + } + + /** + * Get traversable list using lowercase 'id' key and 'regions' result wrapper + * + * Delegates to parent implementation which uses overridden helper methods: + * - determineKeyId() returns 'id' instead of 'ID' + * - extractElementsFromBatchResult() unwraps 'regions' key + * - getReferenceFieldPath() includes 'regions' in batch reference path + * + * @param array $order + * @param array $filter + * @param array $select + * + * @return Generator + * @throws BaseException + * @throws \Bitrix24\SDK\Core\Exceptions\TransportException + */ + #[\Override] + public function getTraversableList( + string $apiMethod, + ?array $order = [], + ?array $filter = [], + ?array $select = [], + ?int $limit = null, + ?array $additionalParameters = null + ): Generator { + yield from parent::getTraversableList($apiMethod, $order, $filter, $select, $limit, $additionalParameters); + } + + /** + * Update entity items with batch call + * + * The documentgenerator.region.update method uses 'id' (lowercase) + * instead of the standard 'ID' key used by most other REST methods. + * + * Update elements in array with structure: + * element_id => [ + * 'fields' => [] // required: region fields to update + * ] + * + * @param array> $entityItems + * + * @return Generator|ResponseData[] + * @throws BaseException + */ + #[\Override] + public function updateEntityItems(string $apiMethod, array $entityItems): Generator + { + $this->logger->debug( + 'updateEntityItems.start', + [ + 'apiMethod' => $apiMethod, + 'entityItems' => $entityItems, + ] + ); + + try { + $this->clearCommands(); + + foreach ($entityItems as $entityItemId => $entityItem) { + if (!is_int($entityItemId)) { + throw new InvalidArgumentException( + sprintf( + 'invalid type «%s» of region id «%s», the id must be integer type', + gettype($entityItemId), + $entityItemId + ) + ); + } + + if (!array_key_exists('fields', $entityItem)) { + throw new InvalidArgumentException( + sprintf('array key «fields» not found in entity item with id %s', $entityItemId) + ); + } + + $cmdArguments = [ + 'id' => $entityItemId, + 'fields' => $entityItem['fields'], + ]; + + $this->registerCommand($apiMethod, $cmdArguments); + } + + foreach ($this->getTraversable(true) as $cnt => $updatedItemResult) { + yield $cnt => $updatedItemResult; + } + } catch (InvalidArgumentException $exception) { + $errorMessage = sprintf('batch update entity items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + throw $exception; + } catch (\Throwable $exception) { + $errorMessage = sprintf('batch update entity items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + + throw new BaseException($errorMessage, $exception->getCode(), $exception); + } + + $this->logger->debug('updateEntityItems.finish'); + } + + /** + * Delete entity items with batch call + * + * @return Generator|ResponseData[] + * @throws BaseException + */ + #[\Override] + public function deleteEntityItems( + string $apiMethod, + array $entityItemId, + ?array $additionalParameters = null + ): Generator { + $this->logger->debug( + 'deleteEntityItems.start', + [ + 'apiMethod' => $apiMethod, + 'entityItems' => $entityItemId, + 'additionalParameters' => $additionalParameters, + ] + ); + + try { + $this->clearCommands(); + foreach ($entityItemId as $cnt => $code) { + if (!is_int($code)) { + throw new InvalidArgumentException( + sprintf( + 'invalid type «%s» of region 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/Documentgenerator/Region/Result/AddedRegionBatchResult.php b/src/Services/Documentgenerator/Region/Result/AddedRegionBatchResult.php new file mode 100644 index 00000000..e1ddbf6d --- /dev/null +++ b/src/Services/Documentgenerator/Region/Result/AddedRegionBatchResult.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Documentgenerator\Region\Result; + +use Bitrix24\SDK\Core\Result\AddedItemBatchResult; + +/** + * Class AddedRegionBatchResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Region\Result + */ +class AddedRegionBatchResult extends AddedItemBatchResult +{ + #[\Override] + public function getId(): int + { + return (int)$this->getResponseData()->getResult()['region']['id']; + } +} diff --git a/src/Services/Documentgenerator/Region/Result/AddedRegionResult.php b/src/Services/Documentgenerator/Region/Result/AddedRegionResult.php new file mode 100644 index 00000000..5ff433a5 --- /dev/null +++ b/src/Services/Documentgenerator/Region/Result/AddedRegionResult.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\Documentgenerator\Region\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AddedItemResult; + +/** + * Class AddedRegionResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Region\Result + */ +class AddedRegionResult extends AddedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function getId(): int + { + return (int)$this->getCoreResponse()->getResponseData()->getResult()['region']['id']; + } +} diff --git a/src/Services/Documentgenerator/Region/Result/DeletedRegionBatchResult.php b/src/Services/Documentgenerator/Region/Result/DeletedRegionBatchResult.php new file mode 100644 index 00000000..163044b8 --- /dev/null +++ b/src/Services/Documentgenerator/Region/Result/DeletedRegionBatchResult.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Documentgenerator\Region\Result; + +use Bitrix24\SDK\Core\Result\DeletedItemBatchResult; + +/** + * Class DeletedRegionBatchResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Region\Result + */ +class DeletedRegionBatchResult extends DeletedItemBatchResult +{ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Region/Result/DeletedRegionResult.php b/src/Services/Documentgenerator/Region/Result/DeletedRegionResult.php new file mode 100644 index 00000000..d5cf0a68 --- /dev/null +++ b/src/Services/Documentgenerator/Region/Result/DeletedRegionResult.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\Documentgenerator\Region\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\DeletedItemResult; + +/** + * Class DeletedRegionResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Region\Result + */ +class DeletedRegionResult extends DeletedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getCoreResponse()->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Region/Result/RegionItemResult.php b/src/Services/Documentgenerator/Region/Result/RegionItemResult.php new file mode 100644 index 00000000..283eaa14 --- /dev/null +++ b/src/Services/Documentgenerator/Region/Result/RegionItemResult.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\Documentgenerator\Region\Result; + +use Bitrix24\SDK\Core\Result\AbstractAnnotatedItem; + +/** + * Class RegionItemResult + * + * @property-read int $id + * @property-read string $title + * @property-read string $languageId + * @property-read string|null $formatDate + * @property-read string|null $formatDatetime + * @property-read string|null $formatName + * @property-read array $phrases + */ +class RegionItemResult extends AbstractAnnotatedItem +{ +} diff --git a/src/Services/Documentgenerator/Region/Result/RegionResult.php b/src/Services/Documentgenerator/Region/Result/RegionResult.php new file mode 100644 index 00000000..26d9ee1f --- /dev/null +++ b/src/Services/Documentgenerator/Region/Result/RegionResult.php @@ -0,0 +1,38 @@ + + * + * 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\Documentgenerator\Region\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class RegionResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Region\Result + */ +class RegionResult extends AbstractResult +{ + /** + * @throws BaseException + */ + public function region(): RegionItemResult + { + $result = $this->getCoreResponse()->getResponseData()->getResult(); + if (!empty($result['region']) && is_array($result['region'])) { + $result = $result['region']; + } + + return new RegionItemResult($result); + } +} diff --git a/src/Services/Documentgenerator/Region/Result/RegionsResult.php b/src/Services/Documentgenerator/Region/Result/RegionsResult.php new file mode 100644 index 00000000..c3344469 --- /dev/null +++ b/src/Services/Documentgenerator/Region/Result/RegionsResult.php @@ -0,0 +1,47 @@ + + * + * 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\Documentgenerator\Region\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class RegionsResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Region\Result + */ +class RegionsResult extends AbstractResult +{ + /** + * @return RegionItemResult[] + * @throws BaseException + */ + public function getRegions(): array + { + $items = []; + $source = []; + + $result = $this->getCoreResponse()->getResponseData()->getResult(); + + if (!empty($result['regions']) && is_array($result['regions'])) { + $source = $result['regions']; + } + + foreach ($source as $item) { + $items[] = new RegionItemResult($item); + } + + return $items; + } +} diff --git a/src/Services/Documentgenerator/Region/Result/UpdatedRegionBatchResult.php b/src/Services/Documentgenerator/Region/Result/UpdatedRegionBatchResult.php new file mode 100644 index 00000000..6bb2a719 --- /dev/null +++ b/src/Services/Documentgenerator/Region/Result/UpdatedRegionBatchResult.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Documentgenerator\Region\Result; + +use Bitrix24\SDK\Core\Result\UpdatedItemBatchResult; + +/** + * Class UpdatedRegionBatchResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Region\Result + */ +class UpdatedRegionBatchResult extends UpdatedItemBatchResult +{ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Region/Result/UpdatedRegionResult.php b/src/Services/Documentgenerator/Region/Result/UpdatedRegionResult.php new file mode 100644 index 00000000..26711f82 --- /dev/null +++ b/src/Services/Documentgenerator/Region/Result/UpdatedRegionResult.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\Documentgenerator\Region\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\UpdatedItemResult; + +/** + * Class UpdatedRegionResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Region\Result + */ +class UpdatedRegionResult extends UpdatedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getCoreResponse()->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Region/Service/Batch.php b/src/Services/Documentgenerator/Region/Service/Batch.php new file mode 100644 index 00000000..44f6041a --- /dev/null +++ b/src/Services/Documentgenerator/Region/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\Documentgenerator\Region\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\Documentgenerator\Region\Result\AddedRegionBatchResult; +use Bitrix24\SDK\Services\Documentgenerator\Region\Result\DeletedRegionBatchResult; +use Bitrix24\SDK\Services\Documentgenerator\Region\Result\RegionItemResult; +use Bitrix24\SDK\Services\Documentgenerator\Region\Result\UpdatedRegionBatchResult; +use Generator; +use Psr\Log\LoggerInterface; + +#[ApiBatchServiceMetadata(new Scope(['documentgenerator']))] +class Batch +{ + /** + * Batch constructor + */ + public function __construct(protected BatchOperationsInterface $batch, protected LoggerInterface $log) + { + } + + /** + * Batch list method for regions + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/region/document-generator-region-list.html + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'documentgenerator.region.list', + 'https://apidocs.bitrix24.com/api-reference/document-generator/region/document-generator-region-list.html', + 'Batch list method for regions' + )] + public function list(?int $limit = null): Generator + { + $this->log->debug( + 'batchList', + [ + 'limit' => $limit, + ] + ); + + $regionListGenerator = $this->batch->getTraversableListWithCount( + 'documentgenerator.region.list', + [], + [], + [], + $limit + ); + foreach ($regionListGenerator as $key => $value) { + yield $key => new RegionItemResult($value); + } + } + + /** + * Batch adding regions + * + * @param array $regions + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'documentgenerator.region.add', + 'https://apidocs.bitrix24.com/api-reference/document-generator/region/document-generator-region-add.html', + 'Batch adding regions' + )] + public function add(array $regions): Generator + { + $items = []; + foreach ($regions as $item) { + $items[] = [ + 'fields' => $item, + ]; + } + + foreach ($this->batch->addEntityItems('documentgenerator.region.add', $items) as $key => $item) { + yield $key => new AddedRegionBatchResult($item); + } + } + + /** + * Batch update regions + * + * Update elements in array with structure + * id => [ // Region id + * 'fields' => [] // Region fields to update + * ] + * + * @param array $entityItems + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'documentgenerator.region.update', + 'https://apidocs.bitrix24.com/api-reference/document-generator/region/document-generator-region-update.html', + 'Update in batch mode a list of regions' + )] + public function update(array $entityItems): Generator + { + foreach ( + $this->batch->updateEntityItems( + 'documentgenerator.region.update', + $entityItems + ) as $key => $item + ) { + yield $key => new UpdatedRegionBatchResult($item); + } + } + + /** + * Batch delete regions + * + * @param int[] $regionId + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'documentgenerator.region.delete', + 'https://apidocs.bitrix24.com/api-reference/document-generator/region/document-generator-region-delete.html', + 'Batch delete regions' + )] + public function delete(array $regionId): Generator + { + foreach ( + $this->batch->deleteEntityItems( + 'documentgenerator.region.delete', + $regionId + ) as $key => $item + ) { + yield $key => new DeletedRegionBatchResult($item); + } + } +} diff --git a/src/Services/Documentgenerator/Region/Service/Region.php b/src/Services/Documentgenerator/Region/Service/Region.php new file mode 100644 index 00000000..1422f85b --- /dev/null +++ b/src/Services/Documentgenerator/Region/Service/Region.php @@ -0,0 +1,182 @@ + + * + * 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\Documentgenerator\Region\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\Documentgenerator\Region\Result\AddedRegionResult; +use Bitrix24\SDK\Services\Documentgenerator\Region\Result\DeletedRegionResult; +use Bitrix24\SDK\Services\Documentgenerator\Region\Result\RegionResult; +use Bitrix24\SDK\Services\Documentgenerator\Region\Result\RegionsResult; +use Bitrix24\SDK\Services\Documentgenerator\Region\Result\UpdatedRegionResult; +use Psr\Log\LoggerInterface; + +#[ApiServiceMetadata(new Scope(['documentgenerator']))] +class Region extends AbstractService +{ + /** + * Region constructor + */ + public function __construct(public Batch $batch, CoreInterface $core, LoggerInterface $logger) + { + parent::__construct($core, $logger); + } + + /** + * Creates a new region + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/region/document-generator-region-add.html + * + * @param array{ + * languageId: string, + * title: string + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.region.add', + 'https://apidocs.bitrix24.com/api-reference/document-generator/region/document-generator-region-add.html', + 'Creates a new region' + )] + public function add(array $fields): AddedRegionResult + { + return new AddedRegionResult( + $this->core->call( + 'documentgenerator.region.add', + [ + 'fields' => $fields, + ] + ) + ); + } + + /** + * Updates an existing region with new values + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/region/document-generator-region-update.html + * + * @param array{ + * languageId?: string, + * title?: string + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.region.update', + 'https://apidocs.bitrix24.com/api-reference/document-generator/region/document-generator-region-update.html', + 'Updates an existing region with new values' + )] + public function update(int $id, array $fields): UpdatedRegionResult + { + return new UpdatedRegionResult( + $this->core->call( + 'documentgenerator.region.update', + [ + 'id' => $id, + 'fields' => $fields, + ] + ) + ); + } + + /** + * Returns information about the region by its identifier + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/region/document-generator-region-get.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.region.get', + 'https://apidocs.bitrix24.com/api-reference/document-generator/region/document-generator-region-get.html', + 'Returns information about the region by its identifier' + )] + public function get(int $id): RegionResult + { + return new RegionResult( + $this->core->call('documentgenerator.region.get', ['id' => $id]) + ); + } + + /** + * Returns a list of regions + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/region/document-generator-region-list.html + * + * @param int $start Offset for pagination + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.region.list', + 'https://apidocs.bitrix24.com/api-reference/document-generator/region/document-generator-region-list.html', + 'Returns a list of regions' + )] + public function list(int $start = 0): RegionsResult + { + return new RegionsResult( + $this->core->call( + 'documentgenerator.region.list', + [ + 'start' => $start, + ] + ) + ); + } + + /** + * Deletes a region + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/region/document-generator-region-delete.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.region.delete', + 'https://apidocs.bitrix24.com/api-reference/document-generator/region/document-generator-region-delete.html', + 'Deletes a region' + )] + public function delete(int $id): DeletedRegionResult + { + return new DeletedRegionResult( + $this->core->call( + 'documentgenerator.region.delete', + ['id' => $id] + ) + ); + } + + /** + * Count regions + * + * @throws BaseException + * @throws TransportException + */ + public function count(): int + { + return count($this->list()->getRegions()); + } +} diff --git a/src/Services/Documentgenerator/Role/Batch.php b/src/Services/Documentgenerator/Role/Batch.php new file mode 100644 index 00000000..dcb6ab9a --- /dev/null +++ b/src/Services/Documentgenerator/Role/Batch.php @@ -0,0 +1,241 @@ + + * + * 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\Documentgenerator\Role; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Response\DTO\ResponseData; +use Generator; + +/** + * Class Batch + * + * Overrides base Batch to handle parameter naming differences in documentgenerator.role.* REST methods: + * - delete uses 'id' instead of 'ID' + * - update uses 'id' instead of 'ID' + * - list results are wrapped in 'roles' key and use lowercase 'id' + * + * @package Bitrix24\SDK\Services\Documentgenerator\Role + */ +class Batch extends \Bitrix24\SDK\Core\Batch +{ + /** + * Determines the ID key — lowercase 'id' for document generator role + */ + #[\Override] + protected function determineKeyId(string $apiMethod, ?array $additionalParameters): string + { + return 'id'; + } + + /** + * Extracts elements from batch result, unwrapping the 'roles' key + */ + #[\Override] + protected function extractElementsFromBatchResult(ResponseData $responseData, bool $isCrmItemsInBatch): array + { + $resultData = $responseData->getResult(); + + if (array_key_exists('roles', $resultData) && is_array($resultData['roles'])) { + return $resultData['roles']; + } + + return $resultData; + } + + /** + * Returns reference field path including 'roles' wrapper for batch query chaining + */ + #[\Override] + protected function getReferenceFieldPath(string $prevCommandId, int $lastIndex, string $keyId, bool $isCrmItemsInBatch): string + { + return sprintf('$result[%s][roles][%d][%s]', $prevCommandId, $lastIndex, $keyId); + } + + /** + * Get traversable list using lowercase 'id' key and 'roles' result wrapper + * + * Delegates to parent implementation which uses overridden helper methods: + * - determineKeyId() returns 'id' instead of 'ID' + * - extractElementsFromBatchResult() unwraps 'roles' key + * - getReferenceFieldPath() includes 'roles' in batch reference path + * + * @param array $order + * @param array $filter + * @param array $select + * + * @return Generator + * @throws BaseException + * @throws \Bitrix24\SDK\Core\Exceptions\TransportException + */ + #[\Override] + public function getTraversableList( + string $apiMethod, + ?array $order = [], + ?array $filter = [], + ?array $select = [], + ?int $limit = null, + ?array $additionalParameters = null + ): Generator { + yield from parent::getTraversableList($apiMethod, $order, $filter, $select, $limit, $additionalParameters); + } + + /** + * Update entity items with batch call + * + * The documentgenerator.role.update method uses 'id' (lowercase) + * instead of the standard 'ID' key used by most other REST methods. + * + * Update elements in array with structure: + * element_id => [ + * 'fields' => [] // required: role fields to update + * ] + * + * @param array> $entityItems + * + * @return Generator|ResponseData[] + * @throws BaseException + */ + #[\Override] + public function updateEntityItems(string $apiMethod, array $entityItems): Generator + { + $this->logger->debug( + 'updateEntityItems.start', + [ + 'apiMethod' => $apiMethod, + 'entityItems' => $entityItems, + ] + ); + + try { + $this->clearCommands(); + + foreach ($entityItems as $entityItemId => $entityItem) { + if (!is_int($entityItemId)) { + throw new InvalidArgumentException( + sprintf( + 'invalid type «%s» of role id «%s», the id must be integer type', + gettype($entityItemId), + $entityItemId + ) + ); + } + + if (!array_key_exists('fields', $entityItem)) { + throw new InvalidArgumentException( + sprintf('array key «fields» not found in entity item with id %s', $entityItemId) + ); + } + + $cmdArguments = [ + 'id' => $entityItemId, + 'fields' => $entityItem['fields'], + ]; + + $this->registerCommand($apiMethod, $cmdArguments); + } + + foreach ($this->getTraversable(true) as $cnt => $updatedItemResult) { + yield $cnt => $updatedItemResult; + } + } catch (InvalidArgumentException $exception) { + $errorMessage = sprintf('batch update entity items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + throw $exception; + } catch (\Throwable $exception) { + $errorMessage = sprintf('batch update entity items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + + throw new BaseException($errorMessage, $exception->getCode(), $exception); + } + + $this->logger->debug('updateEntityItems.finish'); + } + + /** + * Delete entity items with batch call + * + * @return Generator|ResponseData[] + * @throws BaseException + */ + #[\Override] + public function deleteEntityItems( + string $apiMethod, + array $entityItemId, + ?array $additionalParameters = null + ): Generator { + $this->logger->debug( + 'deleteEntityItems.start', + [ + 'apiMethod' => $apiMethod, + 'entityItems' => $entityItemId, + 'additionalParameters' => $additionalParameters, + ] + ); + + try { + $this->clearCommands(); + foreach ($entityItemId as $cnt => $code) { + if (!is_int($code)) { + throw new InvalidArgumentException( + sprintf( + 'invalid type «%s» of role 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/Documentgenerator/Role/Result/AddedRoleBatchResult.php b/src/Services/Documentgenerator/Role/Result/AddedRoleBatchResult.php new file mode 100644 index 00000000..67a55cf7 --- /dev/null +++ b/src/Services/Documentgenerator/Role/Result/AddedRoleBatchResult.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Documentgenerator\Role\Result; + +use Bitrix24\SDK\Core\Result\AddedItemBatchResult; + +/** + * Class AddedRoleBatchResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Role\Result + */ +class AddedRoleBatchResult extends AddedItemBatchResult +{ + #[\Override] + public function getId(): int + { + return (int)$this->getResponseData()->getResult()['role']['id']; + } +} diff --git a/src/Services/Documentgenerator/Role/Result/AddedRoleResult.php b/src/Services/Documentgenerator/Role/Result/AddedRoleResult.php new file mode 100644 index 00000000..3638674b --- /dev/null +++ b/src/Services/Documentgenerator/Role/Result/AddedRoleResult.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\Documentgenerator\Role\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AddedItemResult; + +/** + * Class AddedRoleResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Role\Result + */ +class AddedRoleResult extends AddedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function getId(): int + { + return (int)$this->getCoreResponse()->getResponseData()->getResult()['role']['id']; + } +} diff --git a/src/Services/Documentgenerator/Role/Result/DeletedRoleBatchResult.php b/src/Services/Documentgenerator/Role/Result/DeletedRoleBatchResult.php new file mode 100644 index 00000000..fccb4943 --- /dev/null +++ b/src/Services/Documentgenerator/Role/Result/DeletedRoleBatchResult.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Documentgenerator\Role\Result; + +use Bitrix24\SDK\Core\Result\DeletedItemBatchResult; + +/** + * Class DeletedRoleBatchResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Role\Result + */ +class DeletedRoleBatchResult extends DeletedItemBatchResult +{ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Role/Result/DeletedRoleResult.php b/src/Services/Documentgenerator/Role/Result/DeletedRoleResult.php new file mode 100644 index 00000000..d7864915 --- /dev/null +++ b/src/Services/Documentgenerator/Role/Result/DeletedRoleResult.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\Documentgenerator\Role\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\DeletedItemResult; + +/** + * Class DeletedRoleResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Role\Result + */ +class DeletedRoleResult extends DeletedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getCoreResponse()->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Role/Result/FillAccessesResult.php b/src/Services/Documentgenerator/Role/Result/FillAccessesResult.php new file mode 100644 index 00000000..20916af0 --- /dev/null +++ b/src/Services/Documentgenerator/Role/Result/FillAccessesResult.php @@ -0,0 +1,37 @@ + + * + * 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\Documentgenerator\Role\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class FillAccessesResult + * + * Result of documentgenerator.role.fillaccesses. + * The API returns null on success; isSuccess() returns true when no exception was thrown. + * + * @package Bitrix24\SDK\Services\Documentgenerator\Role\Result + */ +class FillAccessesResult extends AbstractResult +{ + /** + * @throws BaseException + */ + public function isSuccess(): bool + { + // API returns null on success; array cast of [null] is truthy + return (bool)$this->getCoreResponse()->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Role/Result/RoleItemResult.php b/src/Services/Documentgenerator/Role/Result/RoleItemResult.php new file mode 100644 index 00000000..8c92551b --- /dev/null +++ b/src/Services/Documentgenerator/Role/Result/RoleItemResult.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\Documentgenerator\Role\Result; + +use Bitrix24\SDK\Core\Result\AbstractAnnotatedItem; + +/** + * Class RoleItemResult + * + * @property-read int $id + * @property-read string $name + * @property-read string $code + * @property-read array|null $permissions + */ +class RoleItemResult extends AbstractAnnotatedItem +{ +} diff --git a/src/Services/Documentgenerator/Role/Result/RoleResult.php b/src/Services/Documentgenerator/Role/Result/RoleResult.php new file mode 100644 index 00000000..08370905 --- /dev/null +++ b/src/Services/Documentgenerator/Role/Result/RoleResult.php @@ -0,0 +1,38 @@ + + * + * 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\Documentgenerator\Role\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class RoleResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Role\Result + */ +class RoleResult extends AbstractResult +{ + /** + * @throws BaseException + */ + public function role(): RoleItemResult + { + $result = $this->getCoreResponse()->getResponseData()->getResult(); + if (!empty($result['role']) && is_array($result['role'])) { + $result = $result['role']; + } + + return new RoleItemResult($result); + } +} diff --git a/src/Services/Documentgenerator/Role/Result/RolesResult.php b/src/Services/Documentgenerator/Role/Result/RolesResult.php new file mode 100644 index 00000000..0599b056 --- /dev/null +++ b/src/Services/Documentgenerator/Role/Result/RolesResult.php @@ -0,0 +1,47 @@ + + * + * 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\Documentgenerator\Role\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class RolesResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Role\Result + */ +class RolesResult extends AbstractResult +{ + /** + * @return RoleItemResult[] + * @throws BaseException + */ + public function getRoles(): array + { + $items = []; + $source = []; + + $result = $this->getCoreResponse()->getResponseData()->getResult(); + + if (!empty($result['roles']) && is_array($result['roles'])) { + $source = $result['roles']; + } + + foreach ($source as $item) { + $items[] = new RoleItemResult($item); + } + + return $items; + } +} diff --git a/src/Services/Documentgenerator/Role/Result/UpdatedRoleBatchResult.php b/src/Services/Documentgenerator/Role/Result/UpdatedRoleBatchResult.php new file mode 100644 index 00000000..bc1ad0bc --- /dev/null +++ b/src/Services/Documentgenerator/Role/Result/UpdatedRoleBatchResult.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Documentgenerator\Role\Result; + +use Bitrix24\SDK\Core\Result\UpdatedItemBatchResult; + +/** + * Class UpdatedRoleBatchResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Role\Result + */ +class UpdatedRoleBatchResult extends UpdatedItemBatchResult +{ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Role/Result/UpdatedRoleResult.php b/src/Services/Documentgenerator/Role/Result/UpdatedRoleResult.php new file mode 100644 index 00000000..fc85b871 --- /dev/null +++ b/src/Services/Documentgenerator/Role/Result/UpdatedRoleResult.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\Documentgenerator\Role\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\UpdatedItemResult; + +/** + * Class UpdatedRoleResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Role\Result + */ +class UpdatedRoleResult extends UpdatedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getCoreResponse()->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Role/Service/Batch.php b/src/Services/Documentgenerator/Role/Service/Batch.php new file mode 100644 index 00000000..3d2daf97 --- /dev/null +++ b/src/Services/Documentgenerator/Role/Service/Batch.php @@ -0,0 +1,157 @@ + + * + * 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\Documentgenerator\Role\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\Documentgenerator\Role\Result\AddedRoleBatchResult; +use Bitrix24\SDK\Services\Documentgenerator\Role\Result\DeletedRoleBatchResult; +use Bitrix24\SDK\Services\Documentgenerator\Role\Result\RoleItemResult; +use Bitrix24\SDK\Services\Documentgenerator\Role\Result\UpdatedRoleBatchResult; +use Generator; +use Psr\Log\LoggerInterface; + +#[ApiBatchServiceMetadata(new Scope(['documentgenerator']))] +class Batch +{ + /** + * Batch constructor + */ + public function __construct(protected BatchOperationsInterface $batch, protected LoggerInterface $log) + { + } + + /** + * Batch list method for roles + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/role/document-generator-role-list.html + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'documentgenerator.role.list', + 'https://apidocs.bitrix24.com/api-reference/document-generator/role/document-generator-role-list.html', + 'Batch list method for roles' + )] + public function list(?int $limit = null): Generator + { + $this->log->debug( + 'batchList', + [ + 'limit' => $limit, + ] + ); + + $roleListGenerator = $this->batch->getTraversableListWithCount( + 'documentgenerator.role.list', + [], + [], + [], + $limit + ); + foreach ($roleListGenerator as $key => $value) { + yield $key => new RoleItemResult($value); + } + } + + /** + * Batch adding roles + * + * @param array $roles + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'documentgenerator.role.add', + 'https://apidocs.bitrix24.com/api-reference/document-generator/role/document-generator-role-add.html', + 'Batch adding roles' + )] + public function add(array $roles): Generator + { + $items = []; + foreach ($roles as $item) { + $items[] = [ + 'fields' => $item, + ]; + } + + foreach ($this->batch->addEntityItems('documentgenerator.role.add', $items) as $key => $item) { + yield $key => new AddedRoleBatchResult($item); + } + } + + /** + * Batch update roles + * + * Update elements in array with structure: + * id => [ // Role id + * 'fields' => [] // Role fields to update + * ] + * + * @param array $entityItems + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'documentgenerator.role.update', + 'https://apidocs.bitrix24.com/api-reference/document-generator/role/document-generator-role-update.html', + 'Update in batch mode a list of roles' + )] + public function update(array $entityItems): Generator + { + foreach ( + $this->batch->updateEntityItems( + 'documentgenerator.role.update', + $entityItems + ) as $key => $item + ) { + yield $key => new UpdatedRoleBatchResult($item); + } + } + + /** + * Batch delete roles + * + * @param int[] $roleId + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'documentgenerator.role.delete', + 'https://apidocs.bitrix24.com/api-reference/document-generator/role/document-generator-role-delete.html', + 'Batch delete roles' + )] + public function delete(array $roleId): Generator + { + foreach ( + $this->batch->deleteEntityItems( + 'documentgenerator.role.delete', + $roleId + ) as $key => $item + ) { + yield $key => new DeletedRoleBatchResult($item); + } + } +} diff --git a/src/Services/Documentgenerator/Role/Service/Role.php b/src/Services/Documentgenerator/Role/Service/Role.php new file mode 100644 index 00000000..fac6cb2f --- /dev/null +++ b/src/Services/Documentgenerator/Role/Service/Role.php @@ -0,0 +1,210 @@ + + * + * 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\Documentgenerator\Role\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\Documentgenerator\Role\Result\AddedRoleResult; +use Bitrix24\SDK\Services\Documentgenerator\Role\Result\DeletedRoleResult; +use Bitrix24\SDK\Services\Documentgenerator\Role\Result\FillAccessesResult; +use Bitrix24\SDK\Services\Documentgenerator\Role\Result\RoleResult; +use Bitrix24\SDK\Services\Documentgenerator\Role\Result\RolesResult; +use Bitrix24\SDK\Services\Documentgenerator\Role\Result\UpdatedRoleResult; +use Psr\Log\LoggerInterface; + +#[ApiServiceMetadata(new Scope(['documentgenerator']))] +class Role extends AbstractService +{ + /** + * Role constructor + */ + public function __construct(public Batch $batch, CoreInterface $core, LoggerInterface $logger) + { + parent::__construct($core, $logger); + } + + /** + * Creates a new role + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/role/document-generator-role-add.html + * + * @param array{ + * name: string, + * code?: string, + * permissions?: array + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.role.add', + 'https://apidocs.bitrix24.com/api-reference/document-generator/role/document-generator-role-add.html', + 'Creates a new role' + )] + public function add(array $fields): AddedRoleResult + { + return new AddedRoleResult( + $this->core->call( + 'documentgenerator.role.add', + [ + 'fields' => $fields, + ] + ) + ); + } + + /** + * Updates an existing role with new values + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/role/document-generator-role-update.html + * + * @param array{ + * name?: string, + * code?: string, + * permissions?: array + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.role.update', + 'https://apidocs.bitrix24.com/api-reference/document-generator/role/document-generator-role-update.html', + 'Updates an existing role with new values' + )] + public function update(int $id, array $fields): UpdatedRoleResult + { + return new UpdatedRoleResult( + $this->core->call( + 'documentgenerator.role.update', + [ + 'id' => $id, + 'fields' => $fields, + ] + ) + ); + } + + /** + * Returns information about the role by its identifier + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/role/document-generator-role-get.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.role.get', + 'https://apidocs.bitrix24.com/api-reference/document-generator/role/document-generator-role-get.html', + 'Returns information about the role by its identifier' + )] + public function get(int $id): RoleResult + { + return new RoleResult( + $this->core->call('documentgenerator.role.get', ['id' => $id]) + ); + } + + /** + * Returns a list of roles + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/role/document-generator-role-list.html + * + * @param int $start Offset for pagination + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.role.list', + 'https://apidocs.bitrix24.com/api-reference/document-generator/role/document-generator-role-list.html', + 'Returns a list of roles' + )] + public function list(int $start = 0): RolesResult + { + return new RolesResult( + $this->core->call( + 'documentgenerator.role.list', + [ + 'start' => $start, + ] + ) + ); + } + + /** + * Deletes a role + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/role/document-generator-role-delete.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.role.delete', + 'https://apidocs.bitrix24.com/api-reference/document-generator/role/document-generator-role-delete.html', + 'Deletes a role' + )] + public function delete(int $id): DeletedRoleResult + { + return new DeletedRoleResult( + $this->core->call( + 'documentgenerator.role.delete', + ['id' => $id] + ) + ); + } + + /** + * Completely replaces the role-to-access-code binding map + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/role/document-generator-role-fill-accesses.html + * + * @param array $accesses Array of role-to-access-code bindings + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.role.fillaccesses', + 'https://apidocs.bitrix24.com/api-reference/document-generator/role/document-generator-role-fill-accesses.html', + 'Completely replaces the role-to-access-code binding map' + )] + public function fillAccesses(array $accesses): FillAccessesResult + { + return new FillAccessesResult( + $this->core->call( + 'documentgenerator.role.fillaccesses', + ['accesses' => $accesses] + ) + ); + } + + /** + * Count roles + * + * @throws BaseException + * @throws TransportException + */ + public function count(): int + { + return count($this->list()->getRoles()); + } +} diff --git a/src/Services/Documentgenerator/Template/Batch.php b/src/Services/Documentgenerator/Template/Batch.php new file mode 100644 index 00000000..406d1dbd --- /dev/null +++ b/src/Services/Documentgenerator/Template/Batch.php @@ -0,0 +1,241 @@ + + * + * 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\Documentgenerator\Template; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Response\DTO\ResponseData; +use Generator; + +/** + * Class Batch + * + * Overrides base Batch to handle parameter naming differences in documentgenerator.template.* REST methods: + * - delete uses 'id' instead of 'ID' + * - update uses 'id' instead of 'ID' + * - list results are wrapped in 'templates' key and use lowercase 'id' + * + * @package Bitrix24\SDK\Services\Documentgenerator\Template + */ +class Batch extends \Bitrix24\SDK\Core\Batch +{ + /** + * Determines the ID key — lowercase 'id' for document generator template + */ + #[\Override] + protected function determineKeyId(string $apiMethod, ?array $additionalParameters): string + { + return 'id'; + } + + /** + * Extracts elements from batch result, unwrapping the 'templates' key + */ + #[\Override] + protected function extractElementsFromBatchResult(ResponseData $responseData, bool $isCrmItemsInBatch): array + { + $resultData = $responseData->getResult(); + + if (array_key_exists('templates', $resultData) && is_array($resultData['templates'])) { + return $resultData['templates']; + } + + return $resultData; + } + + /** + * Returns reference field path including 'templates' wrapper for batch query chaining + */ + #[\Override] + protected function getReferenceFieldPath(string $prevCommandId, int $lastIndex, string $keyId, bool $isCrmItemsInBatch): string + { + return sprintf('$result[%s][templates][%d][%s]', $prevCommandId, $lastIndex, $keyId); + } + + /** + * Get traversable list using lowercase 'id' key and 'templates' result wrapper + * + * Delegates to parent implementation which uses overridden helper methods: + * - determineKeyId() returns 'id' instead of 'ID' + * - extractElementsFromBatchResult() unwraps 'templates' key + * - getReferenceFieldPath() includes 'templates' in batch reference path + * + * @param array $order + * @param array $filter + * @param array $select + * + * @return Generator + * @throws BaseException + * @throws \Bitrix24\SDK\Core\Exceptions\TransportException + */ + #[\Override] + public function getTraversableList( + string $apiMethod, + ?array $order = [], + ?array $filter = [], + ?array $select = [], + ?int $limit = null, + ?array $additionalParameters = null + ): Generator { + yield from parent::getTraversableList($apiMethod, $order, $filter, $select, $limit, $additionalParameters); + } + + /** + * Update entity items with batch call + * + * The documentgenerator.template.update method uses 'id' (lowercase) + * instead of the standard 'ID' key used by most other REST methods. + * + * Update elements in array with structure: + * element_id => [ + * 'fields' => [] // required: template fields to update + * ] + * + * @param array> $entityItems + * + * @return Generator|ResponseData[] + * @throws BaseException + */ + #[\Override] + public function updateEntityItems(string $apiMethod, array $entityItems): Generator + { + $this->logger->debug( + 'updateEntityItems.start', + [ + 'apiMethod' => $apiMethod, + 'entityItems' => $entityItems, + ] + ); + + try { + $this->clearCommands(); + + foreach ($entityItems as $entityItemId => $entityItem) { + if (!is_int($entityItemId)) { + throw new InvalidArgumentException( + sprintf( + 'invalid type «%s» of template id «%s», the id must be integer type', + gettype($entityItemId), + $entityItemId + ) + ); + } + + if (!array_key_exists('fields', $entityItem)) { + throw new InvalidArgumentException( + sprintf('array key «fields» not found in entity item with id %s', $entityItemId) + ); + } + + $cmdArguments = [ + 'id' => $entityItemId, + 'fields' => $entityItem['fields'], + ]; + + $this->registerCommand($apiMethod, $cmdArguments); + } + + foreach ($this->getTraversable(true) as $cnt => $updatedItemResult) { + yield $cnt => $updatedItemResult; + } + } catch (InvalidArgumentException $exception) { + $errorMessage = sprintf('batch update entity items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + throw $exception; + } catch (\Throwable $exception) { + $errorMessage = sprintf('batch update entity items: %s', $exception->getMessage()); + $this->logger->error( + $errorMessage, + [ + 'trace' => $exception->getTrace(), + ] + ); + + throw new BaseException($errorMessage, $exception->getCode(), $exception); + } + + $this->logger->debug('updateEntityItems.finish'); + } + + /** + * Delete entity items with batch call + * + * @return Generator|ResponseData[] + * @throws BaseException + */ + #[\Override] + public function deleteEntityItems( + string $apiMethod, + array $entityItemId, + ?array $additionalParameters = null + ): Generator { + $this->logger->debug( + 'deleteEntityItems.start', + [ + 'apiMethod' => $apiMethod, + 'entityItems' => $entityItemId, + 'additionalParameters' => $additionalParameters, + ] + ); + + try { + $this->clearCommands(); + foreach ($entityItemId as $cnt => $code) { + if (!is_int($code)) { + throw new InvalidArgumentException( + sprintf( + 'invalid type «%s» of template 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/Documentgenerator/Template/Result/AddedTemplateBatchResult.php b/src/Services/Documentgenerator/Template/Result/AddedTemplateBatchResult.php new file mode 100644 index 00000000..6c78414f --- /dev/null +++ b/src/Services/Documentgenerator/Template/Result/AddedTemplateBatchResult.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Documentgenerator\Template\Result; + +use Bitrix24\SDK\Core\Result\AddedItemBatchResult; + +/** + * Class AddedTemplateBatchResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Template\Result + */ +class AddedTemplateBatchResult extends AddedItemBatchResult +{ + #[\Override] + public function getId(): int + { + return (int)$this->getResponseData()->getResult()['template']['id']; + } +} diff --git a/src/Services/Documentgenerator/Template/Result/AddedTemplateResult.php b/src/Services/Documentgenerator/Template/Result/AddedTemplateResult.php new file mode 100644 index 00000000..9301a1d2 --- /dev/null +++ b/src/Services/Documentgenerator/Template/Result/AddedTemplateResult.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\Documentgenerator\Template\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AddedItemResult; + +/** + * Class AddedTemplateResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Template\Result + */ +class AddedTemplateResult extends AddedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function getId(): int + { + return (int)$this->getCoreResponse()->getResponseData()->getResult()['template']['id']; + } +} diff --git a/src/Services/Documentgenerator/Template/Result/DeletedTemplateBatchResult.php b/src/Services/Documentgenerator/Template/Result/DeletedTemplateBatchResult.php new file mode 100644 index 00000000..5e4723ec --- /dev/null +++ b/src/Services/Documentgenerator/Template/Result/DeletedTemplateBatchResult.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Documentgenerator\Template\Result; + +use Bitrix24\SDK\Core\Result\DeletedItemBatchResult; + +/** + * Class DeletedTemplateBatchResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Template\Result + */ +class DeletedTemplateBatchResult extends DeletedItemBatchResult +{ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Template/Result/DeletedTemplateResult.php b/src/Services/Documentgenerator/Template/Result/DeletedTemplateResult.php new file mode 100644 index 00000000..0d60fdbe --- /dev/null +++ b/src/Services/Documentgenerator/Template/Result/DeletedTemplateResult.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\Documentgenerator\Template\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\DeletedItemResult; + +/** + * Class DeletedTemplateResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Template\Result + */ +class DeletedTemplateResult extends DeletedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getCoreResponse()->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Template/Result/TemplateFieldsResult.php b/src/Services/Documentgenerator/Template/Result/TemplateFieldsResult.php new file mode 100644 index 00000000..788363e6 --- /dev/null +++ b/src/Services/Documentgenerator/Template/Result/TemplateFieldsResult.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\Documentgenerator\Template\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class TemplateFieldsResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Template\Result + */ +class TemplateFieldsResult extends AbstractResult +{ + /** + * @throws BaseException + */ + public function getFieldsDescription(): array + { + $result = $this->getCoreResponse()->getResponseData()->getResult(); + + // API returns fields nested under templateFields key + if (!empty($result['templateFields']) && is_array($result['templateFields'])) { + return $result['templateFields']; + } + + return $result; + } +} diff --git a/src/Services/Documentgenerator/Template/Result/TemplateItemResult.php b/src/Services/Documentgenerator/Template/Result/TemplateItemResult.php new file mode 100644 index 00000000..ff0289ad --- /dev/null +++ b/src/Services/Documentgenerator/Template/Result/TemplateItemResult.php @@ -0,0 +1,47 @@ + + * + * 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\Documentgenerator\Template\Result; + +use Bitrix24\SDK\Core\Result\AbstractAnnotatedItem; +use Carbon\CarbonImmutable; + +/** + * Class TemplateItemResult + * + * @property-read int $id + * @property-read string|null $name + * @property-read string|null $region + * @property-read string|null $code + * @property-read string|null $download + * @property-read string|null $active + * @property-read string|null $moduleId + * @property-read int|null $numeratorId + * @property-read string|null $withStamps + * @property-read array|null $providers + * @property-read array|null $users + * @property-read string|null $isDeleted + * @property-read string|null $isDefault + * @property-read int|null $sort + * @property-read CarbonImmutable|null $createTime + * @property-read CarbonImmutable|null $updateTime + * @property-read int|null $createdBy + * @property-read int|null $updatedBy + * @property-read int|null $fileId + * @property-read string|null $bodyType + * @property-read string|null $productsTableVariant + * @property-read string|null $downloadMachine + */ +class TemplateItemResult extends AbstractAnnotatedItem +{ +} diff --git a/src/Services/Documentgenerator/Template/Result/TemplateResult.php b/src/Services/Documentgenerator/Template/Result/TemplateResult.php new file mode 100644 index 00000000..7fca7e44 --- /dev/null +++ b/src/Services/Documentgenerator/Template/Result/TemplateResult.php @@ -0,0 +1,38 @@ + + * + * 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\Documentgenerator\Template\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class TemplateResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Template\Result + */ +class TemplateResult extends AbstractResult +{ + /** + * @throws BaseException + */ + public function template(): TemplateItemResult + { + $result = $this->getCoreResponse()->getResponseData()->getResult(); + if (!empty($result['template']) && is_array($result['template'])) { + $result = $result['template']; + } + + return new TemplateItemResult($result); + } +} diff --git a/src/Services/Documentgenerator/Template/Result/TemplatesResult.php b/src/Services/Documentgenerator/Template/Result/TemplatesResult.php new file mode 100644 index 00000000..a53b0897 --- /dev/null +++ b/src/Services/Documentgenerator/Template/Result/TemplatesResult.php @@ -0,0 +1,45 @@ + + * + * 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\Documentgenerator\Template\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class TemplatesResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Template\Result + */ +class TemplatesResult extends AbstractResult +{ + /** + * @return TemplateItemResult[] + * @throws BaseException + */ + public function getTemplates(): array + { + $items = []; + $result = $this->getCoreResponse()->getResponseData()->getResult(); + + if (!empty($result['templates']) && is_array($result['templates'])) { + foreach ($result['templates'] as $item) { + if (is_array($item)) { + $items[] = new TemplateItemResult($item); + } + } + } + + return $items; + } +} diff --git a/src/Services/Documentgenerator/Template/Result/UpdatedTemplateBatchResult.php b/src/Services/Documentgenerator/Template/Result/UpdatedTemplateBatchResult.php new file mode 100644 index 00000000..051758c5 --- /dev/null +++ b/src/Services/Documentgenerator/Template/Result/UpdatedTemplateBatchResult.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Documentgenerator\Template\Result; + +use Bitrix24\SDK\Core\Result\UpdatedItemBatchResult; + +/** + * Class UpdatedTemplateBatchResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Template\Result + */ +class UpdatedTemplateBatchResult extends UpdatedItemBatchResult +{ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Template/Result/UpdatedTemplateResult.php b/src/Services/Documentgenerator/Template/Result/UpdatedTemplateResult.php new file mode 100644 index 00000000..bd9da693 --- /dev/null +++ b/src/Services/Documentgenerator/Template/Result/UpdatedTemplateResult.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\Documentgenerator\Template\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\UpdatedItemResult; + +/** + * Class UpdatedTemplateResult + * + * @package Bitrix24\SDK\Services\Documentgenerator\Template\Result + */ +class UpdatedTemplateResult extends UpdatedItemResult +{ + /** + * @throws BaseException + */ + #[\Override] + public function isSuccess(): bool + { + return (bool)$this->getCoreResponse()->getResponseData()->getResult(); + } +} diff --git a/src/Services/Documentgenerator/Template/Service/Batch.php b/src/Services/Documentgenerator/Template/Service/Batch.php new file mode 100644 index 00000000..a703cedb --- /dev/null +++ b/src/Services/Documentgenerator/Template/Service/Batch.php @@ -0,0 +1,171 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\Documentgenerator\Template\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\Documentgenerator\Template\Result\AddedTemplateBatchResult; +use Bitrix24\SDK\Services\Documentgenerator\Template\Result\DeletedTemplateBatchResult; +use Bitrix24\SDK\Services\Documentgenerator\Template\Result\TemplateItemResult; +use Bitrix24\SDK\Services\Documentgenerator\Template\Result\UpdatedTemplateBatchResult; +use Generator; +use Psr\Log\LoggerInterface; + +#[ApiBatchServiceMetadata(new Scope(['documentgenerator']))] +class Batch +{ + /** + * Batch constructor + */ + public function __construct(protected BatchOperationsInterface $batch, protected LoggerInterface $log) + { + } + + /** + * Batch list method for templates + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-list.html + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'documentgenerator.template.list', + 'https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-list.html', + 'Batch list method for templates' + )] + 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 + $templateListGenerator = $this->batch->getTraversableListWithCount( + 'documentgenerator.template.list', + [], + [], + [], + $limit + ); + foreach ($templateListGenerator as $key => $value) { + yield $key => new TemplateItemResult($value); + } + } + + /** + * Batch adding templates + * + * @param array $templates + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-add.html + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'documentgenerator.template.add', + 'https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-add.html', + 'Batch adding templates' + )] + public function add(array $templates): Generator + { + $items = []; + foreach ($templates as $item) { + $items[] = [ + 'fields' => $item, + ]; + } + + foreach ($this->batch->addEntityItems('documentgenerator.template.add', $items) as $key => $item) { + yield $key => new AddedTemplateBatchResult($item); + } + } + + /** + * Batch update templates + * + * Update elements in array with structure + * id => [ // Template id + * 'fields' => [] // Template fields to update + * ] + * + * @param array $entityItems + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-update.html + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'documentgenerator.template.update', + 'https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-update.html', + 'Update in batch mode a list of templates' + )] + public function update(array $entityItems): Generator + { + foreach ( + $this->batch->updateEntityItems( + 'documentgenerator.template.update', + $entityItems + ) as $key => $item + ) { + yield $key => new UpdatedTemplateBatchResult($item); + } + } + + /** + * Batch delete templates + * + * @param int[] $templateId + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-delete.html + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'documentgenerator.template.delete', + 'https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-delete.html', + 'Batch delete templates' + )] + public function delete(array $templateId): Generator + { + foreach ( + $this->batch->deleteEntityItems( + 'documentgenerator.template.delete', + $templateId + ) as $key => $item + ) { + yield $key => new DeletedTemplateBatchResult($item); + } + } +} diff --git a/src/Services/Documentgenerator/Template/Service/Template.php b/src/Services/Documentgenerator/Template/Service/Template.php new file mode 100644 index 00000000..f0896b12 --- /dev/null +++ b/src/Services/Documentgenerator/Template/Service/Template.php @@ -0,0 +1,245 @@ + + * + * 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\Documentgenerator\Template\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\Documentgenerator\Template\Result\AddedTemplateResult; +use Bitrix24\SDK\Services\Documentgenerator\Template\Result\DeletedTemplateResult; +use Bitrix24\SDK\Services\Documentgenerator\Template\Result\TemplateFieldsResult; +use Bitrix24\SDK\Services\Documentgenerator\Template\Result\TemplateResult; +use Bitrix24\SDK\Services\Documentgenerator\Template\Result\TemplatesResult; +use Bitrix24\SDK\Services\Documentgenerator\Template\Result\UpdatedTemplateResult; +use Psr\Log\LoggerInterface; + +#[ApiServiceMetadata(new Scope(['documentgenerator']))] +class Template extends AbstractService +{ + /** + * Template constructor + */ + public function __construct(public Batch $batch, CoreInterface $core, LoggerInterface $logger) + { + parent::__construct($core, $logger); + } + + /** + * Adds a new template + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-add.html + * + * @param array{ + * name: string, + * numeratorId: int, + * region: string, + * fileId?: int, + * file?: string, + * code?: string, + * users?: array, + * active?: string, + * withStamps?: string, + * sort?: int + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.template.add', + 'https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-add.html', + 'Adds a new template' + )] + public function add(array $fields): AddedTemplateResult + { + return new AddedTemplateResult( + $this->core->call( + 'documentgenerator.template.add', + [ + 'fields' => $fields, + ] + ) + ); + } + + /** + * Updates an existing template + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-update.html + * + * @param int $id Template identifier + * @param array{ + * name?: string, + * file?: string, + * numeratorId?: int, + * region?: string, + * code?: string, + * users?: array, + * providers?: array, + * active?: string, + * withStamps?: string, + * sort?: int + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.template.update', + 'https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-update.html', + 'Updates an existing template' + )] + public function update(int $id, array $fields): UpdatedTemplateResult + { + return new UpdatedTemplateResult( + $this->core->call( + 'documentgenerator.template.update', + [ + 'id' => $id, + 'fields' => $fields, + ] + ) + ); + } + + /** + * Returns information about the template by its identifier + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-get.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.template.get', + 'https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-get.html', + 'Returns information about the template by its identifier' + )] + public function get(int $id): TemplateResult + { + return new TemplateResult( + $this->core->call( + 'documentgenerator.template.get', + ['id' => $id] + ) + ); + } + + /** + * Returns a list of templates + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-list.html + * + * @param array $filter Filter parameters + * @param array $order Order parameters + * @param array $select Fields to select + * @param int $start Offset for pagination + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.template.list', + 'https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-list.html', + 'Returns a list of templates' + )] + public function list(array $filter = [], array $order = [], array $select = [], int $start = 0): TemplatesResult + { + $params = [ + 'start' => $start, + ]; + + if ($filter !== []) { + $params['filter'] = $filter; + } + + if ($order !== []) { + $params['order'] = $order; + } + + if ($select !== []) { + $params['select'] = $select; + } + + return new TemplatesResult( + $this->core->call( + 'documentgenerator.template.list', + $params + ) + ); + } + + /** + * Deletes a template + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-delete.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.template.delete', + 'https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-delete.html', + 'Deletes a template' + )] + public function delete(int $id): DeletedTemplateResult + { + return new DeletedTemplateResult( + $this->core->call( + 'documentgenerator.template.delete', + ['id' => $id] + ) + ); + } + + /** + * Returns the description of template fields + * + * @link https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-get-fields.html + * + * @param int $id Template identifier + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'documentgenerator.template.getfields', + 'https://apidocs.bitrix24.com/api-reference/document-generator/templates/document-generator-template-get-fields.html', + 'Returns the description of template fields' + )] + public function getFields(int $id): TemplateFieldsResult + { + return new TemplateFieldsResult( + $this->core->call( + 'documentgenerator.template.getfields', + ['id' => $id] + ) + ); + } + + /** + * Count templates + * + * @throws BaseException + * @throws TransportException + */ + public function count(): int + { + return $this->list()->getCoreResponse()->getResponseData()->getPagination()->getTotal(); + } +} diff --git a/src/Services/ServiceBuilder.php b/src/Services/ServiceBuilder.php index 417ca0d6..d2f6f3cb 100644 --- a/src/Services/ServiceBuilder.php +++ b/src/Services/ServiceBuilder.php @@ -18,6 +18,7 @@ use Bitrix24\SDK\Core\Contracts\CoreInterface; use Bitrix24\SDK\Services\AI\AIServiceBuilder; use Bitrix24\SDK\Services\Catalog\CatalogServiceBuilder; +use Bitrix24\SDK\Services\Documentgenerator\DocumentgeneratorServiceBuilder; use Bitrix24\SDK\Services\CRM\CRMServiceBuilder; use Bitrix24\SDK\Services\Disk\DiskServiceBuilder; use Bitrix24\SDK\Services\Entity\EntityServiceBuilder; @@ -395,4 +396,18 @@ public function getLegacyServiceBuilder(): LegacyServiceBuilder return $this->serviceCache[__METHOD__]; } + public function getDocumentgeneratorScope(): DocumentgeneratorServiceBuilder + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new DocumentgeneratorServiceBuilder( + $this->core, + $this->batch, + $this->bulkItemsReader, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } + } diff --git a/tests/CustomAssertions/CustomBitrix24Assertions.php b/tests/CustomAssertions/CustomBitrix24Assertions.php index 8e379fa5..8538071e 100644 --- a/tests/CustomAssertions/CustomBitrix24Assertions.php +++ b/tests/CustomAssertions/CustomBitrix24Assertions.php @@ -23,9 +23,10 @@ use Carbon\CarbonImmutable; use MoneyPHP\Percentage\Percentage; use Typhoon\Reflection\TyphoonReflector; -use function Typhoon\Type\stringify; use Money\Currency; +use function Typhoon\Type\stringify; + trait CustomBitrix24Assertions { /** @@ -62,13 +63,19 @@ protected function assertBitrix24ResultItemFieldsTypeCastMatchAnnotations( get_debug_type($value) ); + // For nullable union types like "Carbon\CarbonImmutable|null", strip null before assertInstanceOf + $classStr = implode('|', array_values(array_filter( + explode('|', $typeStr), + static fn (string $t): bool => $t !== 'null' + ))); + match (true) { str_contains($typeStr, 'array') => $this->assertIsArray($value, $message), str_contains($typeStr, 'bool') => $this->assertIsBool($value, $message), str_contains($typeStr, 'int') => $this->assertIsInt($value, $message), str_contains($typeStr, 'float') => $this->assertIsFloat($value, $message), str_contains($typeStr, 'string') => $this->assertIsString($value, $message), - default => $this->assertInstanceOf($typeStr, $value, $message), + default => $this->assertInstanceOf($classStr, $value, $message), }; } } diff --git a/tests/Integration/Services/Documentgenerator/Document/Service/BatchTest.php b/tests/Integration/Services/Documentgenerator/Document/Service/BatchTest.php new file mode 100644 index 00000000..c6ac7e57 --- /dev/null +++ b/tests/Integration/Services/Documentgenerator/Document/Service/BatchTest.php @@ -0,0 +1,203 @@ + + * + * 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\Documentgenerator\Document\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Documentgenerator\Document\Service\Document; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\TestCase; +use Faker; + +/** + * Class BatchTest + * + * @package Bitrix24\SDK\Tests\Integration\Services\Documentgenerator\Document\Service + */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Bitrix24\SDK\Services\Documentgenerator\Document\Service\Batch::class)] +class BatchTest extends TestCase +{ + protected Document $documentService; + + private Faker\Generator $faker; + + /** + * @throws InvalidArgumentException + */ + #[\Override] + protected function setUp(): void + { + $this->documentService = Factory::getServiceBuilder()->getDocumentgeneratorScope()->document(); + $this->faker = Faker\Factory::create(); + } + + /** + * Helper: get first available non-CRM documentgenerator template. + * Returns array with keys: id, providerClassName + * + * @return array{id: int, providerClassName: string} + * + * @throws BaseException + * @throws TransportException + */ + private function getFirstTemplate(): array + { + $core = Factory::getCore(); + $response = $core->call('documentgenerator.template.list', ['select' => ['id', 'providers']]); + $result = $response->getResponseData()->getResult(); + $templates = $result['templates'] ?? []; + + self::assertNotEmpty($templates, 'At least one documentgenerator template must exist to run this test'); + + $template = array_values($templates)[0]; + $providers = $template['providers'] ?? []; + $providerClassName = empty($providers) ? 'Bitrix\DocumentGenerator\DataProvider\Rest' : (string)$providers[0]; + + return [ + 'id' => (int)$template['id'], + 'providerClassName' => $providerClassName, + ]; + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch list documents')] + public function testBatchList(): void + { + $templateInfo = $this->getFirstTemplate(); + $value = 'SDK_BATCH_TEST_' . $this->faker->uuid(); + + $id = $this->documentService->add( + $templateInfo['id'], + $templateInfo['providerClassName'], + $value + )->getId(); + + $cnt = 0; + foreach ($this->documentService->batch->list(1) as $item) { + $cnt++; + } + + self::assertGreaterThanOrEqual(1, $cnt); + + // Cleanup + $this->documentService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch add documents')] + public function testBatchAdd(): void + { + $templateInfo = $this->getFirstTemplate(); + $items = []; + + for ($i = 1; $i <= 3; $i++) { + $items[] = [ + 'templateId' => $templateInfo['id'], + 'providerClassName' => $templateInfo['providerClassName'], + 'value' => 'SDK_BATCH_ADD_' . $this->faker->uuid(), + ]; + } + + $ids = []; + $cnt = 0; + foreach ($this->documentService->batch->add($items) as $added) { + $cnt++; + $ids[] = $added->getId(); + } + + self::assertEquals(count($items), $cnt); + + // Cleanup + $delCnt = 0; + foreach ($this->documentService->batch->delete($ids) as $deleted) { + $delCnt++; + } + + self::assertEquals(count($items), $delCnt); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch update documents')] + public function testBatchUpdate(): void + { + $templateInfo = $this->getFirstTemplate(); + $docIds = []; + + for ($i = 1; $i <= 3; $i++) { + $value = 'SDK_BATCH_UPD_' . $this->faker->uuid(); + $docIds[] = $this->documentService->add( + $templateInfo['id'], + $templateInfo['providerClassName'], + $value + )->getId(); + } + + $updatePayload = []; + foreach ($docIds as $docId) { + $updatePayload[$docId] = [ + 'values' => [], + 'stampsEnabled' => 1, + ]; + } + + foreach ($this->documentService->batch->update($updatePayload) as $updated) { + $this->assertTrue($updated->isSuccess()); + } + + // Cleanup + foreach ($this->documentService->batch->delete($docIds) as $deleted) { + // consume generator to execute batch deletion + } + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch delete documents')] + public function testBatchDelete(): void + { + $templateInfo = $this->getFirstTemplate(); + $ids = []; + + for ($i = 1; $i <= 3; $i++) { + $value = 'SDK_BATCH_DEL_' . $this->faker->uuid(); + $ids[] = $this->documentService->add( + $templateInfo['id'], + $templateInfo['providerClassName'], + $value + )->getId(); + } + + $delCnt = 0; + foreach ($this->documentService->batch->delete($ids) as $deleted) { + $delCnt++; + } + + self::assertEquals(count($ids), $delCnt); + } +} + + + diff --git a/tests/Integration/Services/Documentgenerator/Document/Service/DocumentTest.php b/tests/Integration/Services/Documentgenerator/Document/Service/DocumentTest.php new file mode 100644 index 00000000..9f78440d --- /dev/null +++ b/tests/Integration/Services/Documentgenerator/Document/Service/DocumentTest.php @@ -0,0 +1,240 @@ + + * + * 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\Documentgenerator\Document\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Documentgenerator\Document\Result\DocumentItemResult; +use Bitrix24\SDK\Services\Documentgenerator\Document\Service\Document; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\TestCase; +use Faker; + +/** + * Class DocumentTest + * + * @package Bitrix24\SDK\Tests\Integration\Services\Documentgenerator\Document\Service + */ +#[CoversMethod(Document::class, 'add')] +#[CoversMethod(Document::class, 'delete')] +#[CoversMethod(Document::class, 'get')] +#[CoversMethod(Document::class, 'list')] +#[CoversMethod(Document::class, 'update')] +#[CoversMethod(Document::class, 'getFields')] +#[CoversMethod(Document::class, 'enablePublicUrl')] +#[CoversMethod(Document::class, 'count')] +#[\PHPUnit\Framework\Attributes\CoversClass(Document::class)] +class DocumentTest extends TestCase +{ + use CustomBitrix24Assertions; + + private Document $documentService; + + private Faker\Generator $faker; + + /** + * @throws InvalidArgumentException + */ + #[\Override] + protected function setUp(): void + { + $this->documentService = Factory::getServiceBuilder()->getDocumentgeneratorScope()->document(); + $this->faker = Faker\Factory::create(); + } + + /** + * Helper: get first available non-CRM documentgenerator template. + * Returns array with keys: id, providerClassName + * + * @return array{id: int, providerClassName: string} + * + * @throws BaseException + * @throws TransportException + */ + private function getFirstTemplate(): array + { + $core = Factory::getCore(); + $response = $core->call('documentgenerator.template.list', ['select' => ['id', 'providers']]); + $result = $response->getResponseData()->getResult(); + $templates = $result['templates'] ?? []; + + self::assertNotEmpty($templates, 'At least one documentgenerator template must exist to run this test'); + + $template = array_values($templates)[0]; + $providers = $template['providers'] ?? []; + $providerClassName = empty($providers) ? 'Bitrix\DocumentGenerator\DataProvider\Rest' : (string)$providers[0]; + + return [ + 'id' => (int)$template['id'], + 'providerClassName' => $providerClassName, + ]; + } + + /** + * Helper: create a document for tests. + * + * @return array{id: int, templateInfo: array{id: int, providerClassName: string}} + * + * @throws BaseException + * @throws TransportException + */ + private function createDocument(): array + { + $templateInfo = $this->getFirstTemplate(); + $value = 'SDK_TEST_' . $this->faker->uuid(); + + $id = $this->documentService->add( + $templateInfo['id'], + $templateInfo['providerClassName'], + $value + )->getId(); + + return ['id' => $id, 'templateInfo' => $templateInfo]; + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testAdd(): void + { + $templateInfo = $this->getFirstTemplate(); + $value = 'SDK_TEST_' . $this->faker->uuid(); + + $id = $this->documentService->add( + $templateInfo['id'], + $templateInfo['providerClassName'], + $value + )->getId(); + + self::assertGreaterThanOrEqual(1, $id); + + // Cleanup + $this->documentService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testGet(): void + { + $doc = $this->createDocument(); + + $documentItemResult = $this->documentService->get($doc['id'])->document(); + self::assertInstanceOf(DocumentItemResult::class, $documentItemResult); + self::assertEquals($doc['id'], $documentItemResult->id); + + // Cleanup + $this->documentService->delete($doc['id']); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testList(): void + { + $doc = $this->createDocument(); + + $list = $this->documentService->list()->getDocuments(); + self::assertIsArray($list); + self::assertGreaterThanOrEqual(1, count($list)); + + // Cleanup + $this->documentService->delete($doc['id']); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testUpdate(): void + { + $doc = $this->createDocument(); + + self::assertTrue( + $this->documentService->update($doc['id'], [], [], 1)->isSuccess() + ); + + // Cleanup + $this->documentService->delete($doc['id']); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testDelete(): void + { + $doc = $this->createDocument(); + + self::assertTrue($this->documentService->delete($doc['id'])->isSuccess()); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testCount(): void + { + $countBefore = $this->documentService->count(); + + $doc = $this->createDocument(); + + $countAfter = $this->documentService->count(); + self::assertEquals($countBefore + 1, $countAfter); + + // Cleanup + $this->documentService->delete($doc['id']); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testGetFields(): void + { + $doc = $this->createDocument(); + + $documentFieldsResult = $this->documentService->getFields($doc['id']); + $fields = $documentFieldsResult->getFieldsDescription(); + + self::assertIsArray($fields); + + // Cleanup + $this->documentService->delete($doc['id']); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testEnablePublicUrl(): void + { + $doc = $this->createDocument(); + + $publicUrlResult = $this->documentService->enablePublicUrl($doc['id']); + self::assertTrue($publicUrlResult->isSuccess()); + + // Cleanup + $this->documentService->delete($doc['id']); + } +} + + + diff --git a/tests/Integration/Services/Documentgenerator/Numerator/Result/NumeratorItemResultAnnotationsTest.php b/tests/Integration/Services/Documentgenerator/Numerator/Result/NumeratorItemResultAnnotationsTest.php new file mode 100644 index 00000000..fe7dc206 --- /dev/null +++ b/tests/Integration/Services/Documentgenerator/Numerator/Result/NumeratorItemResultAnnotationsTest.php @@ -0,0 +1,88 @@ + + * + * 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\Documentgenerator\Numerator\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Documentgenerator\Numerator\Result\NumeratorItemResult; +use Bitrix24\SDK\Services\Documentgenerator\Numerator\Service\Numerator; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(NumeratorItemResult::class)] +class NumeratorItemResultAnnotationsTest extends TestCase +{ + use CustomBitrix24Assertions; + + private Numerator $numeratorService; + + /** + * @throws InvalidArgumentException + */ + #[\Override] + protected function setUp(): void + { + $this->numeratorService = Factory::getServiceBuilder()->getDocumentgeneratorScope()->numerator(); + } + + /** + * Helper: get raw data for the first numerator from the list. + * + * @return array + * + * @throws BaseException + * @throws TransportException + */ + private function getFirstNumeratorRawItem(): array + { + $result = $this->numeratorService->list() + ->getCoreResponse()->getResponseData()->getResult(); + + $numerators = $result['numerators'] ?? []; + self::assertNotEmpty($numerators, 'At least one numerator must exist to run this test'); + + return array_values($numerators)[0]; + } + + #[Test] + #[TestDox('all fields in NumeratorItemResult are annotated in phpdoc and match with raw api response')] + public function testAllSystemFieldsAnnotated(): void + { + $rawItem = $this->getFirstNumeratorRawItem(); + + $this->assertBitrix24AllResultItemFieldsAnnotated( + array_keys($rawItem), + NumeratorItemResult::class + ); + } + + #[Test] + #[TestDox('all fields in NumeratorItemResult have valid type casting in magic getters')] + public function testAllSystemFieldsHasValidTypeAnnotation(): void + { + $rawItem = $this->getFirstNumeratorRawItem(); + $numeratorItemResult = new NumeratorItemResult($rawItem); + + $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations( + $numeratorItemResult, + NumeratorItemResult::class + ); + } +} + diff --git a/tests/Integration/Services/Documentgenerator/Numerator/Service/BatchTest.php b/tests/Integration/Services/Documentgenerator/Numerator/Service/BatchTest.php new file mode 100644 index 00000000..be39d3a6 --- /dev/null +++ b/tests/Integration/Services/Documentgenerator/Numerator/Service/BatchTest.php @@ -0,0 +1,161 @@ + + * + * 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\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\Documentgenerator\Numerator\Service\Numerator; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\TestCase; +use Faker; + +/** + * Class BatchTest + * + * @package Bitrix24\SDK\Tests\Integration\Services\Documentgenerator\Numerator\Service + */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Bitrix24\SDK\Services\Documentgenerator\Numerator\Service\Batch::class)] +class BatchTest extends TestCase +{ + protected Numerator $numeratorService; + + private Faker\Generator $faker; + + /** + * @throws InvalidArgumentException + */ + #[\Override] + protected function setUp(): void + { + $this->numeratorService = Factory::getServiceBuilder()->getDocumentgeneratorScope()->numerator(); + $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' => 'SDK_BATCH_LIST_' . $this->faker->uuid(), + 'template' => 'BLIST-{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 <= 3; $i++) { + $items[] = [ + 'name' => 'SDK_BATCH_ADD_' . $this->faker->uuid(), + 'template' => 'BADD-{NUMBER}', + ]; + } + + $ids = []; + $cnt = 0; + foreach ($this->numeratorService->batch->add($items) as $added) { + $cnt++; + $ids[] = $added->getId(); + } + + self::assertEquals(count($items), $cnt); + + // Cleanup + $delCnt = 0; + foreach ($this->numeratorService->batch->delete($ids) as $deleted) { + $delCnt++; + } + + self::assertEquals(count($items), $delCnt); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch update numerators')] + public function testBatchUpdate(): void + { + $ids = []; + for ($i = 1; $i <= 3; $i++) { + $ids[] = $this->numeratorService->add([ + 'name' => 'SDK_BATCH_UPD_' . $this->faker->uuid(), + 'template' => 'BUPD-{NUMBER}', + ])->getId(); + } + + $updatePayload = []; + foreach ($ids as $id) { + $updatePayload[$id] = [ + 'fields' => [ + 'name' => 'SDK_BATCH_UPD_UPDATED_' . $this->faker->uuid(), + ], + ]; + } + + foreach ($this->numeratorService->batch->update($updatePayload) as $updated) { + $this->assertTrue($updated->isSuccess()); + } + + // Cleanup + foreach ($this->numeratorService->batch->delete($ids) as $deleted) { + unset($deleted); // consume generator to execute batch deletion + } + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch delete numerators')] + public function testBatchDelete(): void + { + $ids = []; + for ($i = 1; $i <= 3; $i++) { + $ids[] = $this->numeratorService->add([ + 'name' => 'SDK_BATCH_DEL_' . $this->faker->uuid(), + 'template' => 'BDEL-{NUMBER}', + ])->getId(); + } + + $delCnt = 0; + foreach ($this->numeratorService->batch->delete($ids) as $deleted) { + $delCnt++; + } + + self::assertEquals(count($ids), $delCnt); + } +} + + + diff --git a/tests/Integration/Services/Documentgenerator/Numerator/Service/NumeratorTest.php b/tests/Integration/Services/Documentgenerator/Numerator/Service/NumeratorTest.php new file mode 100644 index 00000000..777e2162 --- /dev/null +++ b/tests/Integration/Services/Documentgenerator/Numerator/Service/NumeratorTest.php @@ -0,0 +1,161 @@ + + * + * 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\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\Documentgenerator\Numerator\Result\NumeratorItemResult; +use Bitrix24\SDK\Services\Documentgenerator\Numerator\Service\Numerator; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\TestCase; +use Faker; + +/** + * Class NumeratorTest + * + * @package Bitrix24\SDK\Tests\Integration\Services\Documentgenerator\Numerator\Service + */ +#[CoversMethod(Numerator::class, 'add')] +#[CoversMethod(Numerator::class, 'delete')] +#[CoversMethod(Numerator::class, 'get')] +#[CoversMethod(Numerator::class, 'list')] +#[CoversMethod(Numerator::class, 'update')] +#[CoversMethod(Numerator::class, 'count')] +#[\PHPUnit\Framework\Attributes\CoversClass(Numerator::class)] +class NumeratorTest extends TestCase +{ + use CustomBitrix24Assertions; + + private Numerator $numeratorService; + + private Faker\Generator $faker; + + /** + * @throws InvalidArgumentException + */ + #[\Override] + protected function setUp(): void + { + $this->numeratorService = Factory::getServiceBuilder()->getDocumentgeneratorScope()->numerator(); + $this->faker = Faker\Factory::create(); + } + + /** + * Helper: create a test numerator and return its id. + * + * @throws BaseException + * @throws TransportException + */ + private function createNumerator(): int + { + return $this->numeratorService->add([ + 'name' => 'SDK_TEST_' . $this->faker->uuid(), + 'template' => 'TEST-{NUMBER}', + ])->getId(); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testAdd(): void + { + $id = $this->createNumerator(); + self::assertGreaterThanOrEqual(1, $id); + + // Cleanup + $this->numeratorService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testGet(): void + { + $id = $this->createNumerator(); + + $numeratorItemResult = $this->numeratorService->get($id)->numerator(); + self::assertInstanceOf(NumeratorItemResult::class, $numeratorItemResult); + self::assertEquals($id, $numeratorItemResult->id); + + // Cleanup + $this->numeratorService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testList(): void + { + $id = $this->createNumerator(); + + $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 + { + $id = $this->createNumerator(); + + $updatedName = 'SDK_TEST_UPDATED_' . $this->faker->uuid(); + self::assertTrue( + $this->numeratorService->update($id, ['name' => $updatedName])->isSuccess() + ); + + // Cleanup + $this->numeratorService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testDelete(): void + { + $id = $this->createNumerator(); + + self::assertTrue($this->numeratorService->delete($id)->isSuccess()); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testCount(): void + { + $countBefore = $this->numeratorService->count(); + + $id = $this->createNumerator(); + + $countAfter = $this->numeratorService->count(); + self::assertEquals($countBefore + 1, $countAfter); + + // Cleanup + $this->numeratorService->delete($id); + } +} + diff --git a/tests/Integration/Services/Documentgenerator/Region/Result/RegionItemResultAnnotationsTest.php b/tests/Integration/Services/Documentgenerator/Region/Result/RegionItemResultAnnotationsTest.php new file mode 100644 index 00000000..f06deecd --- /dev/null +++ b/tests/Integration/Services/Documentgenerator/Region/Result/RegionItemResultAnnotationsTest.php @@ -0,0 +1,106 @@ + + * + * 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\Documentgenerator\Region\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Documentgenerator\Region\Result\RegionItemResult; +use Bitrix24\SDK\Services\Documentgenerator\Region\Service\Region; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Factory; +use Faker\Factory as FakerFactory; +use Faker\Generator; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(RegionItemResult::class)] +class RegionItemResultAnnotationsTest extends TestCase +{ + use CustomBitrix24Assertions; + + private Region $regionService; + + private Generator $faker; + + /** + * @throws InvalidArgumentException + */ + #[\Override] + protected function setUp(): void + { + $this->regionService = Factory::getServiceBuilder()->getDocumentgeneratorScope()->region(); + $this->faker = FakerFactory::create(); + } + + /** + * Helper: create a region, fetch it via get() to obtain the full field set, then delete it. + * + * NOTE: documentgenerator.region.list() returns a reduced set of fields (code, languageId, title) + * without the numeric id. documentgenerator.region.get() returns the full set (id, languageId, name, code). + * We therefore validate annotations against the get() response. + * + * @return array + * + * @throws BaseException + * @throws TransportException + */ + private function getFirstRegionRawItem(): array + { + $id = $this->regionService->add([ + 'languageId' => 'en', + 'title' => 'SDK_ANNOT_TEST_' . $this->faker->uuid(), + ])->getId(); + + $rawItem = $this->regionService->get($id) + ->getCoreResponse()->getResponseData()->getResult()['region'] ?? []; + + try { + $this->regionService->delete($id); + } catch (\Bitrix24\SDK\Core\Exceptions\BaseException) { + // Server-side delete bug on some portals; cleanup failure must not affect annotations test + } + + self::assertNotEmpty($rawItem, 'get() must return a region item to run this test'); + + return $rawItem; + } + + #[Test] + #[TestDox('all fields in RegionItemResult are annotated in phpdoc and match with raw api response')] + public function testAllSystemFieldsAnnotated(): void + { + $rawItem = $this->getFirstRegionRawItem(); + + $this->assertBitrix24AllResultItemFieldsAnnotated( + array_keys($rawItem), + RegionItemResult::class + ); + } + + #[Test] + #[TestDox('all fields in RegionItemResult have valid type casting in magic getters')] + public function testAllSystemFieldsHasValidTypeAnnotation(): void + { + $rawItem = $this->getFirstRegionRawItem(); + $regionItemResult = new RegionItemResult($rawItem); + + $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations( + $regionItemResult, + RegionItemResult::class + ); + } +} diff --git a/tests/Integration/Services/Documentgenerator/Region/Service/BatchTest.php b/tests/Integration/Services/Documentgenerator/Region/Service/BatchTest.php new file mode 100644 index 00000000..72104a1e --- /dev/null +++ b/tests/Integration/Services/Documentgenerator/Region/Service/BatchTest.php @@ -0,0 +1,184 @@ + + * + * 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\Documentgenerator\Region\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Documentgenerator\Region\Service\Region; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\TestCase; +use Faker; + +/** + * Class BatchTest + * + * @package Bitrix24\SDK\Tests\Integration\Services\Documentgenerator\Region\Service + */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Bitrix24\SDK\Services\Documentgenerator\Region\Service\Batch::class)] +class BatchTest extends TestCase +{ + protected Region $regionService; + + private Faker\Generator $faker; + + /** + * @throws InvalidArgumentException + */ + #[\Override] + protected function setUp(): void + { + $this->regionService = Factory::getServiceBuilder()->getDocumentgeneratorScope()->region(); + $this->faker = Faker\Factory::create(); + } + + /** + * Helper: silently delete a region by id. + * documentgenerator.region.delete has a known server-side bug on some portals. + */ + private function safeDelete(int $id): void + { + try { + $this->regionService->delete($id); + } catch (BaseException) { + // Server-side delete bug; ignored during cleanup + } + } + + /** + * Helper: silently batch-delete regions by ids. + */ + private function safeBatchDelete(array $ids): void + { + try { + foreach ($this->regionService->batch->delete($ids) as $deleted) { + unset($deleted); + } + } catch (BaseException) { + // Server-side delete bug; ignored during cleanup + } + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch list regions')] + public function testBatchList(): void + { + $id = $this->regionService->add([ + 'languageId' => 'en', + 'title' => 'SDK_BATCH_LIST_' . $this->faker->uuid(), + ])->getId(); + + $cnt = 0; + foreach ($this->regionService->batch->list(1) as $item) { + $cnt++; + } + + self::assertGreaterThanOrEqual(1, $cnt); + + // Cleanup + $this->safeDelete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch add regions')] + public function testBatchAdd(): void + { + $items = []; + for ($i = 1; $i <= 3; $i++) { + $items[] = [ + 'languageId' => 'en', + 'title' => 'SDK_BATCH_ADD_' . $this->faker->uuid(), + ]; + } + + $ids = []; + $cnt = 0; + foreach ($this->regionService->batch->add($items) as $added) { + $cnt++; + $ids[] = $added->getId(); + } + + self::assertEquals(count($items), $cnt); + + // Cleanup + $this->safeBatchDelete($ids); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch update regions')] + public function testBatchUpdate(): void + { + $ids = []; + for ($i = 1; $i <= 3; $i++) { + $ids[] = $this->regionService->add([ + 'languageId' => 'en', + 'title' => 'SDK_BATCH_UPD_' . $this->faker->uuid(), + ])->getId(); + } + + $updatePayload = []; + foreach ($ids as $id) { + $updatePayload[$id] = [ + 'fields' => [ + 'title' => 'SDK_BATCH_UPD_UPDATED_' . $this->faker->uuid(), + ], + ]; + } + + foreach ($this->regionService->batch->update($updatePayload) as $updated) { + $this->assertTrue($updated->isSuccess()); + } + + // Cleanup + $this->safeBatchDelete($ids); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch delete regions')] + public function testBatchDelete(): void + { + $ids = []; + for ($i = 1; $i <= 3; $i++) { + $ids[] = $this->regionService->add([ + 'languageId' => 'en', + 'title' => 'SDK_BATCH_DEL_' . $this->faker->uuid(), + ])->getId(); + } + + try { + $delCnt = 0; + foreach ($this->regionService->batch->delete($ids) as $deleted) { + $delCnt++; + } + + self::assertEquals(count($ids), $delCnt); + } catch (BaseException $baseException) { + $this->markTestSkipped( + 'documentgenerator.region.delete has a known server-side bug on this portal: ' . $baseException->getMessage() + ); + } + } +} diff --git a/tests/Integration/Services/Documentgenerator/Region/Service/RegionTest.php b/tests/Integration/Services/Documentgenerator/Region/Service/RegionTest.php new file mode 100644 index 00000000..dd42c00e --- /dev/null +++ b/tests/Integration/Services/Documentgenerator/Region/Service/RegionTest.php @@ -0,0 +1,182 @@ + + * + * 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\Documentgenerator\Region\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Documentgenerator\Region\Result\RegionItemResult; +use Bitrix24\SDK\Services\Documentgenerator\Region\Service\Region; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\TestCase; +use Faker; + +/** + * Class RegionTest + * + * @package Bitrix24\SDK\Tests\Integration\Services\Documentgenerator\Region\Service + */ +#[CoversMethod(Region::class, 'add')] +#[CoversMethod(Region::class, 'delete')] +#[CoversMethod(Region::class, 'get')] +#[CoversMethod(Region::class, 'list')] +#[CoversMethod(Region::class, 'update')] +#[CoversMethod(Region::class, 'count')] +#[\PHPUnit\Framework\Attributes\CoversClass(Region::class)] +class RegionTest extends TestCase +{ + use CustomBitrix24Assertions; + + private Region $regionService; + + private Faker\Generator $faker; + + /** + * @throws InvalidArgumentException + */ + #[\Override] + protected function setUp(): void + { + $this->regionService = Factory::getServiceBuilder()->getDocumentgeneratorScope()->region(); + $this->faker = Faker\Factory::create(); + } + + /** + * Helper: create a test region and return its id. + * + * @throws BaseException + * @throws TransportException + */ + private function createRegion(): int + { + return $this->regionService->add([ + 'languageId' => 'en', + 'title' => 'SDK_TEST_' . $this->faker->uuid(), + ])->getId(); + } + + /** + * Helper: silently delete a region. + * documentgenerator.region.delete has a known server-side bug on some portals + * (class "bitrix\main\orm\eventresult" not found). + * Cleanup failures must not break unrelated test assertions. + */ + private function safeDelete(int $id): void + { + try { + $this->regionService->delete($id); + } catch (BaseException) { + // Server-side delete bug; ignored during cleanup + } + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testAdd(): void + { + $id = $this->createRegion(); + self::assertGreaterThanOrEqual(1, $id); + + // Cleanup + $this->safeDelete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testGet(): void + { + $id = $this->createRegion(); + + $regionItemResult = $this->regionService->get($id)->region(); + self::assertInstanceOf(RegionItemResult::class, $regionItemResult); + self::assertEquals($id, $regionItemResult->id); + + // Cleanup + $this->safeDelete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testList(): void + { + $id = $this->createRegion(); + + $list = $this->regionService->list()->getRegions(); + self::assertIsArray($list); + self::assertGreaterThanOrEqual(1, count($list)); + + // Cleanup + $this->safeDelete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testUpdate(): void + { + $id = $this->createRegion(); + + $updatedName = 'SDK_TEST_UPDATED_' . $this->faker->uuid(); + self::assertTrue( + $this->regionService->update($id, ['title' => $updatedName])->isSuccess() + ); + + // Cleanup + $this->safeDelete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testDelete(): void + { + $id = $this->createRegion(); + + try { + $result = $this->regionService->delete($id); + self::assertTrue($result->isSuccess()); + } catch (BaseException $baseException) { + $this->markTestSkipped( + 'documentgenerator.region.delete has a known server-side bug on this portal: ' . $baseException->getMessage() + ); + } + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testCount(): void + { + $countBefore = $this->regionService->count(); + + $id = $this->createRegion(); + + $countAfter = $this->regionService->count(); + self::assertEquals($countBefore + 1, $countAfter); + + // Cleanup + $this->safeDelete($id); + } +} diff --git a/tests/Integration/Services/Documentgenerator/Role/Result/RoleItemResultAnnotationsTest.php b/tests/Integration/Services/Documentgenerator/Role/Result/RoleItemResultAnnotationsTest.php new file mode 100644 index 00000000..2550e62f --- /dev/null +++ b/tests/Integration/Services/Documentgenerator/Role/Result/RoleItemResultAnnotationsTest.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\Documentgenerator\Role\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Documentgenerator\Role\Result\RoleItemResult; +use Bitrix24\SDK\Services\Documentgenerator\Role\Service\Role; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Factory; +use Faker\Factory as FakerFactory; +use Faker\Generator; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(RoleItemResult::class)] +class RoleItemResultAnnotationsTest extends TestCase +{ + use CustomBitrix24Assertions; + + private Role $roleService; + + private Generator $faker; + + /** + * @throws InvalidArgumentException + */ + #[\Override] + protected function setUp(): void + { + $this->roleService = Factory::getServiceBuilder()->getDocumentgeneratorScope()->role(); + $this->faker = FakerFactory::create(); + } + + /** + * Helper: create a role, fetch it via get() to obtain the full field set, then delete it. + * + * NOTE: documentgenerator.role.list() returns a reduced set of fields (id, name, code) + * without the permissions field. documentgenerator.role.get() returns the full set including permissions. + * We therefore validate annotations against the get() response. + * + * @return array + * + * @throws BaseException + * @throws TransportException + */ + private function getFirstRoleRawItem(): array + { + $id = $this->roleService->add([ + 'name' => 'SDK_ANNOT_TEST_' . $this->faker->uuid(), + ])->getId(); + + $rawItem = $this->roleService->get($id) + ->getCoreResponse()->getResponseData()->getResult()['role'] ?? []; + + try { + $this->roleService->delete($id); + } catch (BaseException) { + // Server-side error during cleanup; must not affect annotations test + } + + self::assertNotEmpty($rawItem, 'get() must return a role item to run this test'); + + return $rawItem; + } + + #[Test] + #[TestDox('all fields in RoleItemResult are annotated in phpdoc and match with raw api response')] + public function testAllSystemFieldsAnnotated(): void + { + $rawItem = $this->getFirstRoleRawItem(); + + $this->assertBitrix24AllResultItemFieldsAnnotated( + array_keys($rawItem), + RoleItemResult::class + ); + } + + #[Test] + #[TestDox('all fields in RoleItemResult have valid type casting in magic getters')] + public function testAllSystemFieldsHasValidTypeAnnotation(): void + { + $rawItem = $this->getFirstRoleRawItem(); + $roleItemResult = new RoleItemResult($rawItem); + + $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations( + $roleItemResult, + RoleItemResult::class + ); + } +} diff --git a/tests/Integration/Services/Documentgenerator/Role/Service/BatchTest.php b/tests/Integration/Services/Documentgenerator/Role/Service/BatchTest.php new file mode 100644 index 00000000..9fad9137 --- /dev/null +++ b/tests/Integration/Services/Documentgenerator/Role/Service/BatchTest.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\Documentgenerator\Role\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Documentgenerator\Role\Service\Role; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\TestCase; +use Faker; + +/** + * Class BatchTest + * + * @package Bitrix24\SDK\Tests\Integration\Services\Documentgenerator\Role\Service + */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Bitrix24\SDK\Services\Documentgenerator\Role\Service\Batch::class)] +class BatchTest extends TestCase +{ + protected Role $roleService; + + private Faker\Generator $faker; + + /** + * @throws InvalidArgumentException + */ + #[\Override] + protected function setUp(): void + { + $this->roleService = Factory::getServiceBuilder()->getDocumentgeneratorScope()->role(); + $this->faker = Faker\Factory::create(); + } + + /** + * Helper: silently delete a role by id. + */ + private function safeDelete(int $id): void + { + try { + $this->roleService->delete($id); + } catch (BaseException) { + // Server-side error; ignored during cleanup + } + } + + /** + * Helper: silently batch-delete roles by ids. + */ + private function safeBatchDelete(array $ids): void + { + try { + foreach ($this->roleService->batch->delete($ids) as $deleted) { + unset($deleted); + } + } catch (BaseException) { + // Server-side error; ignored during cleanup + } + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch list roles')] + public function testBatchList(): void + { + $id = $this->roleService->add([ + 'name' => 'SDK_BATCH_LIST_' . $this->faker->uuid(), + ])->getId(); + + $cnt = 0; + foreach ($this->roleService->batch->list(1) as $item) { + $cnt++; + } + + self::assertGreaterThanOrEqual(1, $cnt); + + // Cleanup + $this->safeDelete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch add roles')] + public function testBatchAdd(): void + { + $items = []; + for ($i = 1; $i <= 3; $i++) { + $items[] = [ + 'name' => 'SDK_BATCH_ADD_' . $this->faker->uuid(), + ]; + } + + $ids = []; + $cnt = 0; + foreach ($this->roleService->batch->add($items) as $added) { + $cnt++; + $ids[] = $added->getId(); + } + + self::assertEquals(count($items), $cnt); + + // Cleanup + $this->safeBatchDelete($ids); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch update roles')] + public function testBatchUpdate(): void + { + $ids = []; + for ($i = 1; $i <= 3; $i++) { + $ids[] = $this->roleService->add([ + 'name' => 'SDK_BATCH_UPD_' . $this->faker->uuid(), + ])->getId(); + } + + $updatePayload = []; + foreach ($ids as $id) { + $updatePayload[$id] = [ + 'fields' => [ + 'name' => 'SDK_BATCH_UPD_UPDATED_' . $this->faker->uuid(), + ], + ]; + } + + foreach ($this->roleService->batch->update($updatePayload) as $updated) { + $this->assertTrue($updated->isSuccess()); + } + + // Cleanup + $this->safeBatchDelete($ids); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch delete roles')] + public function testBatchDelete(): void + { + $ids = []; + for ($i = 1; $i <= 3; $i++) { + $ids[] = $this->roleService->add([ + 'name' => 'SDK_BATCH_DEL_' . $this->faker->uuid(), + ])->getId(); + } + + $delCnt = 0; + foreach ($this->roleService->batch->delete($ids) as $deleted) { + $delCnt++; + } + + self::assertEquals(count($ids), $delCnt); + } +} diff --git a/tests/Integration/Services/Documentgenerator/Role/Service/RoleTest.php b/tests/Integration/Services/Documentgenerator/Role/Service/RoleTest.php new file mode 100644 index 00000000..5f22a2b4 --- /dev/null +++ b/tests/Integration/Services/Documentgenerator/Role/Service/RoleTest.php @@ -0,0 +1,194 @@ + + * + * 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\Documentgenerator\Role\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Documentgenerator\Role\Result\RoleItemResult; +use Bitrix24\SDK\Services\Documentgenerator\Role\Service\Role; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\TestCase; +use Faker; + +/** + * Class RoleTest + * + * @package Bitrix24\SDK\Tests\Integration\Services\Documentgenerator\Role\Service + */ +#[CoversMethod(Role::class, 'add')] +#[CoversMethod(Role::class, 'delete')] +#[CoversMethod(Role::class, 'get')] +#[CoversMethod(Role::class, 'list')] +#[CoversMethod(Role::class, 'update')] +#[CoversMethod(Role::class, 'count')] +#[CoversMethod(Role::class, 'fillAccesses')] +#[\PHPUnit\Framework\Attributes\CoversClass(Role::class)] +class RoleTest extends TestCase +{ + use CustomBitrix24Assertions; + + private Role $roleService; + + private Faker\Generator $faker; + + /** + * @throws InvalidArgumentException + */ + #[\Override] + protected function setUp(): void + { + $this->roleService = Factory::getServiceBuilder()->getDocumentgeneratorScope()->role(); + $this->faker = Faker\Factory::create(); + } + + /** + * Helper: create a test role and return its id. + * + * @throws BaseException + * @throws TransportException + */ + private function createRole(): int + { + return $this->roleService->add([ + 'name' => 'SDK_TEST_' . $this->faker->uuid(), + 'code' => 'SDK_TEST_' . strtoupper(substr(str_replace('-', '_', $this->faker->uuid()), 0, 20)), + ])->getId(); + } + + /** + * Helper: silently delete a role. + */ + private function safeDelete(int $id): void + { + try { + $this->roleService->delete($id); + } catch (BaseException) { + // Server-side error; ignored during cleanup + } + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testAdd(): void + { + $id = $this->createRole(); + self::assertGreaterThanOrEqual(1, $id); + + // Cleanup + $this->safeDelete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testGet(): void + { + $id = $this->createRole(); + + $roleItemResult = $this->roleService->get($id)->role(); + self::assertInstanceOf(RoleItemResult::class, $roleItemResult); + self::assertEquals($id, $roleItemResult->id); + + // Cleanup + $this->safeDelete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testList(): void + { + $id = $this->createRole(); + + $list = $this->roleService->list()->getRoles(); + self::assertIsArray($list); + self::assertGreaterThanOrEqual(1, count($list)); + + // Cleanup + $this->safeDelete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testUpdate(): void + { + $id = $this->createRole(); + + $updatedName = 'SDK_TEST_UPDATED_' . $this->faker->uuid(); + self::assertTrue( + $this->roleService->update($id, ['name' => $updatedName])->isSuccess() + ); + + // Cleanup + $this->safeDelete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testDelete(): void + { + $id = $this->createRole(); + + $deletedRoleResult = $this->roleService->delete($id); + self::assertTrue($deletedRoleResult->isSuccess()); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testCount(): void + { + $countBefore = $this->roleService->count(); + + $id = $this->createRole(); + + $countAfter = $this->roleService->count(); + self::assertEquals($countBefore + 1, $countAfter); + + // Cleanup + $this->safeDelete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testFillAccesses(): void + { + $id = $this->createRole(); + + $fillAccessesResult = $this->roleService->fillAccesses([ + [ + 'roleId' => $id, + 'accessCode' => 'UA', + ], + ]); + self::assertTrue($fillAccessesResult->isSuccess()); + + // Cleanup + $this->safeDelete($id); + } +} diff --git a/tests/Integration/Services/Documentgenerator/Template/Result/TemplateItemResultAnnotationsTest.php b/tests/Integration/Services/Documentgenerator/Template/Result/TemplateItemResultAnnotationsTest.php new file mode 100644 index 00000000..c1b3a056 --- /dev/null +++ b/tests/Integration/Services/Documentgenerator/Template/Result/TemplateItemResultAnnotationsTest.php @@ -0,0 +1,91 @@ + + * + * 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\Documentgenerator\Template\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Documentgenerator\Template\Result\TemplateItemResult; +use Bitrix24\SDK\Services\Documentgenerator\Template\Service\Template; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\TestCase; + +#[CoversClass(TemplateItemResult::class)] +class TemplateItemResultAnnotationsTest extends TestCase +{ + use CustomBitrix24Assertions; + + private Template $templateService; + + /** + * @throws InvalidArgumentException + */ + #[\Override] + protected function setUp(): void + { + $this->templateService = Factory::getServiceBuilder()->getDocumentgeneratorScope()->template(); + } + + /** + * Helper: get raw data for the first template with all fields selected. + * + * @return array + * + * @throws BaseException + * @throws TransportException + */ + private function getFirstTemplateRawItem(): array + { + $result = $this->templateService->list( + [], + [], + ['*', 'users', 'providers'] + )->getCoreResponse()->getResponseData()->getResult(); + + $templates = $result['templates'] ?? []; + self::assertNotEmpty($templates, 'At least one documentgenerator template must exist to run this test'); + + return array_values($templates)[0]; + } + + #[Test] + #[TestDox('all fields in TemplateItemResult are annotated in phpdoc and match with raw api response')] + public function testAllSystemFieldsAnnotated(): void + { + $rawItem = $this->getFirstTemplateRawItem(); + + $this->assertBitrix24AllResultItemFieldsAnnotated( + array_keys($rawItem), + TemplateItemResult::class + ); + } + + #[Test] + #[TestDox('all fields in TemplateItemResult have valid type casting in magic getters')] + public function testAllSystemFieldsHasValidTypeAnnotation(): void + { + $rawItem = $this->getFirstTemplateRawItem(); + $templateItemResult = new TemplateItemResult($rawItem); + + $this->assertBitrix24ResultItemFieldsTypeCastMatchAnnotations( + $templateItemResult, + TemplateItemResult::class + ); + } +} + diff --git a/tests/Integration/Services/Documentgenerator/Template/Service/BatchTest.php b/tests/Integration/Services/Documentgenerator/Template/Service/BatchTest.php new file mode 100644 index 00000000..9c2528d4 --- /dev/null +++ b/tests/Integration/Services/Documentgenerator/Template/Service/BatchTest.php @@ -0,0 +1,57 @@ + + * + * 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\Documentgenerator\Template\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Documentgenerator\Template\Service\Template; +use Bitrix24\SDK\Tests\Integration\Factory; +use PHPUnit\Framework\TestCase; + +/** + * Class BatchTest + * + * @package Bitrix24\SDK\Tests\Integration\Services\Documentgenerator\Template\Service + */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Bitrix24\SDK\Services\Documentgenerator\Template\Service\Batch::class)] +class BatchTest extends TestCase +{ + protected Template $templateService; + + /** + * @throws InvalidArgumentException + */ + #[\Override] + protected function setUp(): void + { + $this->templateService = Factory::getServiceBuilder()->getDocumentgeneratorScope()->template(); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch list templates')] + public function testBatchList(): void + { + $cnt = 0; + foreach ($this->templateService->batch->list() as $item) { + $cnt++; + } + + self::assertGreaterThanOrEqual(0, $cnt); + } +} + diff --git a/tests/Integration/Services/Documentgenerator/Template/Service/TemplateTest.php b/tests/Integration/Services/Documentgenerator/Template/Service/TemplateTest.php new file mode 100644 index 00000000..c3fe8fa3 --- /dev/null +++ b/tests/Integration/Services/Documentgenerator/Template/Service/TemplateTest.php @@ -0,0 +1,207 @@ + + * + * 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\Documentgenerator\Template\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\Documentgenerator\Template\Result\TemplateItemResult; +use Bitrix24\SDK\Services\Documentgenerator\Template\Service\Template; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Factory; +use Faker; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\TestCase; + +/** + * Class TemplateTest + * + * @package Bitrix24\SDK\Tests\Integration\Services\Documentgenerator\Template\Service + */ +#[CoversMethod(Template::class, 'add')] +#[CoversMethod(Template::class, 'update')] +#[CoversMethod(Template::class, 'delete')] +#[CoversMethod(Template::class, 'get')] +#[CoversMethod(Template::class, 'list')] +#[CoversMethod(Template::class, 'getFields')] +#[CoversMethod(Template::class, 'count')] +#[\PHPUnit\Framework\Attributes\CoversClass(Template::class)] +class TemplateTest extends TestCase +{ + use CustomBitrix24Assertions; + + private Template $templateService; + + private Faker\Generator $faker; + + /** + * @throws InvalidArgumentException + */ + #[\Override] + protected function setUp(): void + { + $this->templateService = Factory::getServiceBuilder()->getDocumentgeneratorScope()->template(); + $this->faker = Faker\Factory::create(); + } + + /** + * Helper: creates a minimal HTML template content encoded as base64 for template upload. + * Using HTML format avoids any dependency on the ext-zip PHP extension. + */ + private function createMinimalTemplateBase64(): string + { + $html = '

Test template {Number}

'; + + return base64_encode($html); + } + + /** + * Helper: get the first available template from the system. + * + * @return array{id: int} + * + * @throws BaseException + * @throws TransportException + */ + private function getFirstTemplate(): array + { + $result = $this->templateService->list()->getCoreResponse()->getResponseData()->getResult(); + $templates = $result['templates'] ?? []; + + self::assertNotEmpty($templates, 'At least one documentgenerator template must exist to run this test'); + + $template = array_values($templates)[0]; + + return [ + 'id' => (int)$template['id'], + ]; + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testList(): void + { + $list = $this->templateService->list()->getTemplates(); + self::assertIsArray($list); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testGet(): void + { + $templateInfo = $this->getFirstTemplate(); + + $templateItemResult = $this->templateService->get($templateInfo['id'])->template(); + self::assertInstanceOf(TemplateItemResult::class, $templateItemResult); + self::assertEquals($templateInfo['id'], $templateItemResult->id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testCount(): void + { + $count = $this->templateService->count(); + self::assertGreaterThanOrEqual(0, $count); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testAdd(): void + { + $name = 'tpl-' . $this->faker->uuid(); + $fileContent = $this->createMinimalTemplateBase64(); + + $id = $this->templateService->add([ + 'name' => $name, + 'file' => $fileContent, + 'numeratorId' => 1, + 'region' => 'en', + ])->getId(); + + self::assertGreaterThanOrEqual(1, $id); + + // Cleanup + $this->templateService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testUpdate(): void + { + $name = 'tpl-' . $this->faker->uuid(); + $fileContent = $this->createMinimalTemplateBase64(); + + $id = $this->templateService->add([ + 'name' => $name, + 'file' => $fileContent, + 'numeratorId' => 1, + 'region' => 'en', + ])->getId(); + + $newName = $name . '-updated'; + self::assertTrue( + $this->templateService->update($id, [ + 'name' => $newName, + ])->isSuccess() + ); + + self::assertEquals($newName, $this->templateService->get($id)->template()->name); + + // Cleanup + $this->templateService->delete($id); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testDelete(): void + { + $fileContent = $this->createMinimalTemplateBase64(); + + $id = $this->templateService->add([ + 'name' => 'tpl-' . $this->faker->uuid(), + 'file' => $fileContent, + 'numeratorId' => 1, + 'region' => 'en', + ])->getId(); + + self::assertTrue($this->templateService->delete($id)->isSuccess()); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testGetFields(): void + { + $templateInfo = $this->getFirstTemplate(); + + $templateFieldsResult = $this->templateService->getFields($templateInfo['id']); + $fields = $templateFieldsResult->getFieldsDescription(); + + self::assertIsArray($fields); + } +} +