diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index fcb7e644..a560e05f 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -6,7 +6,7 @@ $finder = Finder::create() ->in(__DIR__ . '/src/Infrastructure/Console/Commands/') - ->in(__DIR__ . '/src/Services/CRM/Lead/Service') + ->in(__DIR__ . '/src/Services/CRM/Item/Service/') ->in(__DIR__ . '/src/Services/CRM/Contact/') ->in(__DIR__ . '/src/Services/CRM/Quote/') ->in(__DIR__ . '/src/Services/CRM/Lead/') diff --git a/CHANGELOG.md b/CHANGELOG.md index feda4457..0aad1cce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ ### Added +- Added service `CRM\Item\Service\ItemDetailsConfiguration` with support methods, + see [add crm.item.details.* methods](https://github.com/bitrix24/b24phpsdk/issues/168): + - `getPersonal` method retrieves the settings of item cards for personal user + - `getGeneral` method retrieves the settings of item cards for all users + - `resetPersonal` method reset for item user settings + - `resetGeneral` method reset all card settings for all users + - `setPersonal` method set card configuration + - `setGeneral` method set card configuration for all users + - `setForceCommonConfigForAll` method set common detail form for All Users - Added service `CRM\Deal\Service\DealDetailsConfiguration` with support methods, see [add crm.deal.details.* methods](https://github.com/bitrix24/b24phpsdk/issues/158): - `getPersonal` method retrieves the settings of deal cards for personal user diff --git a/Makefile b/Makefile index 704c88e8..7504392f 100644 --- a/Makefile +++ b/Makefile @@ -215,6 +215,10 @@ integration_tests_lead_details: integration_tests_scope_automation: docker-compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_scope_automation +.PHONY: integration_tests_item_details +integration_tests_item_details: + docker-compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_item_details + .PHONY: integration_tests_lead_productrows integration_tests_lead_productrows: docker-compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_lead_productrows diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 1cfbf1ca..dfb03075 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -11,6 +11,7 @@ parameters: - tests/Integration/Services/IMOpenLines - tests/Integration/Services/Main - tests/Integration/Services/Placement + - tests/Integration/Services/CRM/Item/Service/ItemDetailsConfigurationTest.php - tests/Integration/Services/CRM/Deal/Service/DealDetailsConfigurationTest.php - tests/Integration/Services/CRM/Lead/Service/LeadDetailsConfigurationTest.php - tests/Integration/Services/CRM/Contact/Service/ContactDetailsConfigurationTest.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 2fd01198..a4e7db1c 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -64,6 +64,12 @@ ./tests/Integration/Services/CRM/Deal/Service/DealRecurringTest.php + + ./tests/Integration/Services/CRM/Item/Service/ItemDetailsConfigurationTest.php + + + ./tests/Integration/Services/CRM/Automation/ + ./tests/Integration/Services/CRM/Lead/Service/LeadDetailsConfigurationTest.php diff --git a/rector.php b/rector.php index c7731d54..73231a4b 100644 --- a/rector.php +++ b/rector.php @@ -36,6 +36,10 @@ __DIR__ . '/tests/Integration/Services/Main', __DIR__ . '/src/Services/Placement', __DIR__ . '/tests/Integration/Services/Placement', + __DIR__ . '/src/Services/CRM/Deal', + __DIR__ . '/tests/Integration/Services/CRM/Deal/Service', + __DIR__ . '/src/Services/CRM/Item/Service', + __DIR__ . '/tests/Integration/Services/CRM/Item/Service', __DIR__ . '/src/Services/CRM/Deal/Service/DealDetailsConfiguration.php', __DIR__ . '/tests/Integration/Services/CRM/Deal/Service/DealDetailsConfigurationTest.php', __DIR__ . '/src/Services/CRM/Contact/Service/ContactDetailsConfiguration.php', diff --git a/src/Services/CRM/CRMServiceBuilder.php b/src/Services/CRM/CRMServiceBuilder.php index 485625e4..e861e598 100644 --- a/src/Services/CRM/CRMServiceBuilder.php +++ b/src/Services/CRM/CRMServiceBuilder.php @@ -72,6 +72,18 @@ public function companyDetailsConfiguration(): Company\Service\CompanyDetailsCon return $this->serviceCache[__METHOD__]; } + public function itemDetailsConfiguration(): Item\Service\ItemDetailsConfiguration + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Item\Service\ItemDetailsConfiguration( + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } + public function dealDetailsConfiguration(): Deal\Service\DealDetailsConfiguration { if (!isset($this->serviceCache[__METHOD__])) { diff --git a/src/Services/CRM/Item/Service/Batch.php b/src/Services/CRM/Item/Service/Batch.php index 546765ab..eceb6c89 100644 --- a/src/Services/CRM/Item/Service/Batch.php +++ b/src/Services/CRM/Item/Service/Batch.php @@ -26,13 +26,8 @@ #[ApiBatchServiceMetadata(new Scope(['crm']))] class Batch { - protected BatchOperationsInterface $batch; - protected LoggerInterface $log; - - public function __construct(BatchOperationsInterface $batch, LoggerInterface $log) + public function __construct(protected BatchOperationsInterface $batch, protected LoggerInterface $log) { - $this->batch = $batch; - $this->log = $log; } /** @@ -83,8 +78,9 @@ public function add(int $entityTypeId, array $items): Generator 'fields' => $item, ]; } + foreach ($this->batch->addEntityItems('crm.item.add', $rawItems) as $key => $item) { yield $key => new ItemItemResult($item->getResult()['item']); } } -} \ No newline at end of file +} diff --git a/src/Services/CRM/Item/Service/Item.php b/src/Services/CRM/Item/Service/Item.php index 5425be81..3efedb78 100644 --- a/src/Services/CRM/Item/Service/Item.php +++ b/src/Services/CRM/Item/Service/Item.php @@ -30,12 +30,9 @@ #[ApiServiceMetadata(new Scope(['crm']))] class Item extends AbstractService { - public Batch $batch; - - public function __construct(Batch $batch, CoreInterface $core, LoggerInterface $log) + public function __construct(public Batch $batch, CoreInterface $core, LoggerInterface $logger) { - parent::__construct($core, $log); - $this->batch = $batch; + parent::__construct($core, $logger); } /** @@ -44,9 +41,6 @@ public function __construct(Batch $batch, CoreInterface $core, LoggerInterface $ * @link https://training.bitrix24.com/rest_help/crm/dynamic/methodscrmitem/crm_item_add.php * * - * @param int $entityTypeId - * @param array $fields - * @return ItemResult * @throws BaseException * @throws TransportException */ @@ -73,10 +67,7 @@ public function add(int $entityTypeId, array $fields): ItemResult * * @link https://training.bitrix24.com/rest_help/crm/dynamic/methodscrmitem/crm_item_delete.php * - * @param int $entityTypeId - * @param int $id * - * @return DeletedItemResult * @throws BaseException * @throws TransportException */ @@ -89,7 +80,8 @@ public function delete(int $entityTypeId, int $id): DeletedItemResult { return new DeletedItemResult( $this->core->call( - 'crm.item.delete', ['entityTypeId' => $entityTypeId, 'id' => $id] + 'crm.item.delete', + ['entityTypeId' => $entityTypeId, 'id' => $id] ) ); } @@ -99,8 +91,6 @@ public function delete(int $entityTypeId, int $id): DeletedItemResult * * @link https://training.bitrix24.com/rest_help/crm/dynamic/methodscrmitem/crm_item_fields.php * - * @param int $entityTypeId - * @return FieldsResult * @throws BaseException * @throws TransportException */ @@ -198,4 +188,4 @@ public function countByFilter(int $entityTypeId, array $filter = []): int { return $this->list($entityTypeId, [], $filter, ['id'], 1)->getCoreResponse()->getResponseData()->getPagination()->getTotal(); } -} \ No newline at end of file +} diff --git a/src/Services/CRM/Item/Service/ItemDetailsConfiguration.php b/src/Services/CRM/Item/Service/ItemDetailsConfiguration.php new file mode 100644 index 00000000..e0f79652 --- /dev/null +++ b/src/Services/CRM/Item/Service/ItemDetailsConfiguration.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\CRM\Item\Service; + +use Bitrix24\SDK\Attributes\ApiEndpointMetadata; +use Bitrix24\SDK\Attributes\ApiServiceMetadata; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Result\UpdatedItemResult; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\CRM\Common\CardFieldConfiguration; +use Bitrix24\SDK\Services\CRM\Common\CardSectionConfiguration; +use Bitrix24\SDK\Services\CRM\Common\Result\ElementCardConfiguration\CardConfigurationsResult; + +#[ApiServiceMetadata(new Scope(['crm']))] +class ItemDetailsConfiguration extends AbstractService +{ + /** + * Get Parameters of CRM Item Detail Configuration for personal user. + * + * @param non-negative-int $entityTypeId + * @param non-negative-int $userId + * @link https://apidocs.bitrix24.com/api-reference/crm/universal/item-details-configuration/index.html + */ + #[ApiEndpointMetadata( + 'crm.item.details.configuration.get', + 'https://apidocs.bitrix24.com/api-reference/crm/universal/item-details-configuration/index.html', + 'Get Parameters of CRM Item Detail Configuration for personal user' + )] + public function getPersonal(int $entityTypeId, int $userId, array $extras = []): CardConfigurationsResult + { + return new CardConfigurationsResult($this->core->call('crm.item.details.configuration.get', [ + 'scope' => 'P', + 'userId' => $userId, + 'entityTypeId' => $entityTypeId, + 'extras' => $extras + ])); + } + + /** + * Get Parameters of CRM Item Detail Configuration for all users. + * + * @param non-negative-int $entityTypeId + * @link https://apidocs.bitrix24.com/api-reference/crm/universal/item-details-configuration/index.html + */ + #[ApiEndpointMetadata( + 'crm.item.details.configuration.get', + 'https://apidocs.bitrix24.com/api-reference/crm/universal/item-details-configuration/index.html', + 'Get Parameters of CRM Item Detail Configuration for all users' + )] + public function getGeneral(int $entityTypeId, array $extras = []): CardConfigurationsResult + { + return new CardConfigurationsResult($this->core->call('crm.item.details.configuration.get', [ + 'scope' => 'C', + 'entityTypeId' => $entityTypeId, + 'extras' => $extras + ])); + } + + /** + * Reset Item Card Parameters for personal user. + * + * @param non-negative-int $entityTypeId + * @param non-negative-int $userId + * @link https://apidocs.bitrix24.com/api-reference/crm/universal/item-details-configuration/crm-item-details-configuration-reset.html + */ + #[ApiEndpointMetadata( + 'crm.item.details.configuration.reset', + 'https://apidocs.bitrix24.com/api-reference/crm/universal/item-details-configuration/crm-item-details-configuration-reset.html', + 'Reset Item Card Parameters for personal user' + )] + public function resetPersonal(int $entityTypeId, int $userId, array $extras = []): UpdatedItemResult + { + return new UpdatedItemResult($this->core->call('crm.item.details.configuration.reset', [ + 'scope' => 'P', + 'userId' => $userId, + 'entityTypeId' => $entityTypeId, + 'extras' => $extras + ])); + } + + /** + * Reset Item Card Parameters for all users. + * + * @param non-negative-int $entityTypeId + * @link https://apidocs.bitrix24.com/api-reference/crm/universal/item-details-configuration/crm-item-details-configuration-reset.html + */ + #[ApiEndpointMetadata( + 'crm.item.details.configuration.reset', + 'https://apidocs.bitrix24.com/api-reference/crm/universal/item-details-configuration/crm-item-details-configuration-reset.html', + 'Reset Item Card Parameters for all users' + )] + public function resetGeneral(int $entityTypeId, array $extras = []): UpdatedItemResult + { + return new UpdatedItemResult($this->core->call('crm.item.details.configuration.reset', [ + 'scope' => 'C', + 'entityTypeId' => $entityTypeId, + 'extras' => $extras + ])); + } + + /** + * Set Parameters for Individual CRM Item Detail Card Configuration + * + * @param CardSectionConfiguration[] $cardConfiguration + * @param non-negative-int $entityTypeId + * @param non-negative-int $userId + * @throws InvalidArgumentException + * @link https://apidocs.bitrix24.com/api-reference/crm/universal/item-details-configuration/crm-item-details-configuration-set.html + */ + #[ApiEndpointMetadata( + 'crm.item.details.configuration.set', + 'https://apidocs.bitrix24.com/api-reference/crm/universal/item-details-configuration/crm-item-details-configuration-set.html', + 'Set Parameters for Individual CRM Item Detail Card Configuration' + )] + public function setPersonal(array $cardConfiguration, int $entityTypeId, int $userId, array $extras = []): UpdatedItemResult + { + $rawData = []; + foreach ($cardConfiguration as $sectionItem) { + if (!$sectionItem instanceof CardSectionConfiguration) { + throw new InvalidArgumentException( + sprintf( + 'card configuration section mus be «%s» type, current type «%s»', + CardFieldConfiguration::class, + gettype($sectionItem) + ) + ); + } + + $rawData[] = $sectionItem->toArray(); + } + + return new UpdatedItemResult($this->core->call('crm.item.details.configuration.set', [ + 'scope' => 'P', + 'userId' => $userId, + 'data' => $rawData, + 'entityTypeId' => $entityTypeId, + 'extras' => $extras + ])); + } + + /** + * Set CRM Item Detail Card Configuration for all users. + * + * @param CardSectionConfiguration[] $cardConfiguration + * @param non-negative-int $entityTypeId + * @throws InvalidArgumentException + * @link https://apidocs.bitrix24.com/api-reference/crm/universal/item-details-configuration/crm-item-details-configuration-set.html + */ + #[ApiEndpointMetadata( + 'crm.item.details.configuration.set', + 'https://apidocs.bitrix24.com/api-reference/crm/universal/item-details-configuration/crm-item-details-configuration-set.html', + 'Set CRM Item Detail Card Configuration for all users' + )] + public function setGeneral(array $cardConfiguration, int $entityTypeId, array $extras = []): UpdatedItemResult + { + $rawData = []; + foreach ($cardConfiguration as $sectionItem) { + if (!$sectionItem instanceof CardSectionConfiguration) { + throw new InvalidArgumentException( + sprintf( + 'card configuration section mus be «%s» type, current type «%s»', + CardFieldConfiguration::class, + gettype($sectionItem) + ) + ); + } + + $rawData[] = $sectionItem->toArray(); + } + + return new UpdatedItemResult($this->core->call('crm.item.details.configuration.set', [ + 'scope' => 'C', + 'data' => $rawData, + 'entityTypeId' => $entityTypeId, + 'extras' => $extras + ])); + } + + /** + * Set Common Detail Form for All Users + * + * @param non-negative-int $entityTypeId + * @throws InvalidArgumentException + * @link https://apidocs.bitrix24.com/api-reference/crm/deals/custom-form/crm-deal-details-configuration-force-common-scope-for-all.html + */ + #[ApiEndpointMetadata( + 'crm.item.details.configuration.forceCommonScopeForAll', + 'https://apidocs.bitrix24.com/api-reference/crm/deals/custom-form/crm-deal-details-configuration-force-common-scope-for-all.html', + 'Set Common Detail Form for All Users ' + )] + public function setForceCommonConfigForAll(int $entityTypeId, array $extras = []): UpdatedItemResult + { + return new UpdatedItemResult($this->core->call('crm.item.details.configuration.forceCommonScopeForAll', [ + 'entityTypeId' => $entityTypeId, + 'extras' => $extras + ])); + } +} diff --git a/tests/Integration/Services/CRM/Item/Service/ItemDetailsConfigurationTest.php b/tests/Integration/Services/CRM/Item/Service/ItemDetailsConfigurationTest.php new file mode 100644 index 00000000..1be61fc9 --- /dev/null +++ b/tests/Integration/Services/CRM/Item/Service/ItemDetailsConfigurationTest.php @@ -0,0 +1,168 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\CRM\Item\Service; + + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\CRM\Common\CardFieldConfiguration; +use Bitrix24\SDK\Services\CRM\Common\CardSectionConfiguration; +use Bitrix24\SDK\Services\CRM\Item\Service\ItemDetailsConfiguration; +use Bitrix24\SDK\Services\ServiceBuilder; +use Bitrix24\SDK\Tests\Integration\Fabric; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\TestCase; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; + +#[CoversClass(ItemDetailsConfiguration::class)] +#[CoversMethod(ItemDetailsConfiguration::class, 'getPersonal')] +#[CoversMethod(ItemDetailsConfiguration::class, 'resetGeneral')] +#[CoversMethod(ItemDetailsConfiguration::class, 'resetPersonal')] +#[CoversMethod(ItemDetailsConfiguration::class, 'setPersonal')] +#[CoversMethod(ItemDetailsConfiguration::class, 'setGeneral')] +#[CoversMethod(ItemDetailsConfiguration::class, 'setForceCommonConfigForAll')] +class ItemDetailsConfigurationTest extends TestCase +{ + use CustomBitrix24Assertions; + + public const ENTITY_TYPE_ID = 2; + + private ServiceBuilder $sb; + + private ItemDetailsConfiguration $itemConfig; + + protected function setUp(): void + { + $this->sb = Fabric::getServiceBuilder(); + $this->itemConfig = $this->sb->getCRMScope()->itemDetailsConfiguration(); + } + + protected function tearDown(): void + { + $this->itemConfig->resetGeneral(self::ENTITY_TYPE_ID); + } + + public function testResetGeneral(): void + { + $this->assertTrue($this->itemConfig->resetGeneral(self::ENTITY_TYPE_ID)->isSuccess()); + } + + public function testResetPersonal(): void + { + $currentUserId = $this->sb->getMainScope()->main()->getCurrentUserProfile()->getUserProfile()->ID; + $this->assertTrue( + $this->itemConfig->resetPersonal(self::ENTITY_TYPE_ID, $currentUserId)->isSuccess() + ); + } + + /** + * @throws TransportException + * @throws InvalidArgumentException + * @throws BaseException + */ + public function testSetPersonal(): void + { + $config = [ + new CardSectionConfiguration('main', 'About the Deal', [ + new CardFieldConfiguration('TITLE'), + new CardFieldConfiguration('OPPORTUNITY_WITH_CURRENCY'), + new CardFieldConfiguration('STAGE_ID'), + new CardFieldConfiguration('BEGINDATE'), + new CardFieldConfiguration('CLOSEDATE'), + new CardFieldConfiguration('CLIENT'), + ]), + new CardSectionConfiguration('additional', 'Additional Information', [ + new CardFieldConfiguration('TYPE_ID'), + new CardFieldConfiguration('SOURCE_ID'), + new CardFieldConfiguration('ASSIGNED_BY_ID'), + new CardFieldConfiguration('COMMENTS'), + ]) + ]; + + $currentUserId = $this->sb->getMainScope()->main()->getCurrentUserProfile()->getUserProfile()->ID; + $this->assertTrue( + $this->itemConfig->setPersonal( + $config, + self::ENTITY_TYPE_ID, + $currentUserId + )->isSuccess() + ); + } + + /** + * @throws TransportException + * @throws InvalidArgumentException + * @throws BaseException + */ + public function testSetGeneral(): void + { + $config = [ + new CardSectionConfiguration('main', 'About the Deal', [ + new CardFieldConfiguration('TITLE'), + new CardFieldConfiguration('OPPORTUNITY_WITH_CURRENCY'), + new CardFieldConfiguration('STAGE_ID'), + new CardFieldConfiguration('BEGINDATE'), + new CardFieldConfiguration('CLOSEDATE'), + new CardFieldConfiguration('CLIENT'), + ]), + new CardSectionConfiguration('additional', 'Additional Information', [ + new CardFieldConfiguration('TYPE_ID'), + new CardFieldConfiguration('SOURCE_ID'), + new CardFieldConfiguration('ASSIGNED_BY_ID'), + new CardFieldConfiguration('COMMENTS'), + ]) + ]; + + $this->assertTrue($this->itemConfig->setGeneral($config, self::ENTITY_TYPE_ID)->isSuccess()); + } + + public function testForceConfigForAll(): void + { + $this->assertTrue($this->itemConfig->setForceCommonConfigForAll(self::ENTITY_TYPE_ID)->isSuccess()); + } + + public function testGetPersonal(): void + { + $config = [ + new CardSectionConfiguration('main', 'About the Deal', [ + new CardFieldConfiguration('TITLE'), + new CardFieldConfiguration('OPPORTUNITY_WITH_CURRENCY'), + new CardFieldConfiguration('STAGE_ID'), + new CardFieldConfiguration('BEGINDATE'), + new CardFieldConfiguration('CLOSEDATE'), + new CardFieldConfiguration('CLIENT'), + ]), + new CardSectionConfiguration('additional', 'Additional Information', [ + new CardFieldConfiguration('TYPE_ID'), + new CardFieldConfiguration('SOURCE_ID'), + new CardFieldConfiguration('ASSIGNED_BY_ID'), + new CardFieldConfiguration('COMMENTS'), + ]) + ]; + + $currentUserId = $this->sb->getMainScope()->main()->getCurrentUserProfile()->getUserProfile()->ID; + $this->assertTrue( + $this->itemConfig->setPersonal( + $config, + self::ENTITY_TYPE_ID, + $currentUserId + )->isSuccess() + ); + + $cardConfigurationsResult = $this->itemConfig->getPersonal(self::ENTITY_TYPE_ID, $currentUserId); + $this->assertIsArray($cardConfigurationsResult->getSections()); + } +}