From 0e685c3b6156d68ce653bd59b19c06e82aa997fb Mon Sep 17 00:00:00 2001 From: kirill Date: Fri, 22 Jul 2022 17:48:46 +0300 Subject: [PATCH 01/94] -test serializer --- composer.json | 8 +++-- .../Telephony/testSerializer/person.php | 35 +++++++++++++++++++ .../Telephony/testSerializer/serializer.php | 21 +++++++++++ 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 src/Services/Telephony/testSerializer/person.php create mode 100644 src/Services/Telephony/testSerializer/serializer.php diff --git a/composer.json b/composer.json index 0a09205e..799df0ef 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "require": { "php": "7.4.*|8.*", "ext-json": "*", + "ext-bcmath": "*", "ext-curl": "*", "psr/log": "^1.1.4 || ^2.0 || ^3.0", "fig/http-message-util": "1.1.*", @@ -26,7 +27,9 @@ "symfony/http-client-contracts": "^2.5 || ^3.1", "symfony/http-foundation": "5.4.* || 6.*", "symfony/event-dispatcher": "5.4.* || 6.*", - "ramsey/uuid": "^4.2.3" + "ramsey/uuid": "^4.2.3", + "moneyphp/money": "3.* || 4.*", + "symfony/serializer": "^6.1" }, "require-dev": { "monolog/monolog": "2.1.*", @@ -36,7 +39,8 @@ "phpstan/phpstan": "1.*", "phpunit/phpunit": "9.5.*", "symfony/stopwatch": "5.4.* || 6.*", - "roave/security-advisories": "dev-master" + "roave/security-advisories": "dev-master", + "ext-intl": "*" }, "autoload": { "psr-4": { diff --git a/src/Services/Telephony/testSerializer/person.php b/src/Services/Telephony/testSerializer/person.php new file mode 100644 index 00000000..4e72acfe --- /dev/null +++ b/src/Services/Telephony/testSerializer/person.php @@ -0,0 +1,35 @@ +name; + } + + public function getLastName() + { + return $this->lastName; + } + + + // Setters + public function setName($name) + { + $this->name = $name; + } + + public function setLastName($lastName) + { + $this->lastName = $lastName; + } + +} \ No newline at end of file diff --git a/src/Services/Telephony/testSerializer/serializer.php b/src/Services/Telephony/testSerializer/serializer.php new file mode 100644 index 00000000..96808e9e --- /dev/null +++ b/src/Services/Telephony/testSerializer/serializer.php @@ -0,0 +1,21 @@ +setName('Kirill'); +$person->setLastName('Hramov'); + +$jsonContent = $serializer->serialize($person, 'json'); + +// $jsonContent contains {"name":"foo","age":99,"sportsperson":false,"createdAt":null} + +echo $jsonContent; // or return it in a Response \ No newline at end of file From 76d68d76bf452a06b3e629b240e3b23c30581c3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Mon, 25 Jul 2022 15:05:18 +0300 Subject: [PATCH 02/94] -add test serialize and deserialize --- composer.json | 4 +- .../Telephony/testSerializer/serializer.php | 21 ------- .../Unit/Services/TestPerson/Person.php | 4 +- tests/Unit/Services/TestPerson/PersonTest.php | 58 +++++++++++++++++++ 4 files changed, 63 insertions(+), 24 deletions(-) delete mode 100644 src/Services/Telephony/testSerializer/serializer.php rename src/Services/Telephony/testSerializer/person.php => tests/Unit/Services/TestPerson/Person.php (86%) create mode 100644 tests/Unit/Services/TestPerson/PersonTest.php diff --git a/composer.json b/composer.json index 799df0ef..fdbf9b3b 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,9 @@ "symfony/event-dispatcher": "5.4.* || 6.*", "ramsey/uuid": "^4.2.3", "moneyphp/money": "3.* || 4.*", - "symfony/serializer": "^6.1" + "symfony/serializer": "6.*||^6.1", + "symfony/routing": "^4.4|^5.3|^6.0", + "symfony/property-access": "6.* || 6.1.*" }, "require-dev": { "monolog/monolog": "2.1.*", diff --git a/src/Services/Telephony/testSerializer/serializer.php b/src/Services/Telephony/testSerializer/serializer.php deleted file mode 100644 index 96808e9e..00000000 --- a/src/Services/Telephony/testSerializer/serializer.php +++ /dev/null @@ -1,21 +0,0 @@ -setName('Kirill'); -$person->setLastName('Hramov'); - -$jsonContent = $serializer->serialize($person, 'json'); - -// $jsonContent contains {"name":"foo","age":99,"sportsperson":false,"createdAt":null} - -echo $jsonContent; // or return it in a Response \ No newline at end of file diff --git a/src/Services/Telephony/testSerializer/person.php b/tests/Unit/Services/TestPerson/Person.php similarity index 86% rename from src/Services/Telephony/testSerializer/person.php rename to tests/Unit/Services/TestPerson/Person.php index 4e72acfe..41c3c19f 100644 --- a/src/Services/Telephony/testSerializer/person.php +++ b/tests/Unit/Services/TestPerson/Person.php @@ -2,9 +2,9 @@ declare(strict_types=1); -namespace Bitrix24\SDK\Services\Telephony\testSerializer; +namespace Bitrix24\SDK\Tests\Unit\Services\TestPerson; -class person +class Person { private string $name; private string $lastName; diff --git a/tests/Unit/Services/TestPerson/PersonTest.php b/tests/Unit/Services/TestPerson/PersonTest.php new file mode 100644 index 00000000..0027610a --- /dev/null +++ b/tests/Unit/Services/TestPerson/PersonTest.php @@ -0,0 +1,58 @@ +setName('Kirill'); + $person->setLastName('Khramov'); + + + $jsonContent = $serializer->serialize($person, 'json'); + + + self::assertNotEmpty($jsonContent); + echo $jsonContent; // or return it in a Response + } + + /** + * @test + */ + public function DeserializeTest():void{ + $encoders = [new XmlEncoder(), new JsonEncoder()]; + $normalizers = [new ObjectNormalizer()]; + + $serializer = new Serializer($normalizers, $encoders); + + $data = << + Kirill + Khramov + false + + EOF; + + $person = $serializer->deserialize($data, Person::class, 'xml'); + var_dump($person); + self::assertNotEmpty($person); + } +} \ No newline at end of file From ef264a96de201b7b44395e90d793cac33de00dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Tue, 26 Jul 2022 18:02:44 +0300 Subject: [PATCH 03/94] -add test normalize -and edit NetworkTimingsParser.php --- .../TransportLayer/NetworkTimingsParser.php | 14 ++++---- tests/Unit/Services/TestPerson/PersonTest.php | 33 +++++++++++++++++++ 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/Infrastructure/HttpClient/TransportLayer/NetworkTimingsParser.php b/src/Infrastructure/HttpClient/TransportLayer/NetworkTimingsParser.php index 696a08a9..9d2b2711 100644 --- a/src/Infrastructure/HttpClient/TransportLayer/NetworkTimingsParser.php +++ b/src/Infrastructure/HttpClient/TransportLayer/NetworkTimingsParser.php @@ -28,12 +28,12 @@ public function __construct(array $httpClientResponseInfo) // get the name lookup time // Time from the start until the name resolving was completed. // When a redirect is followed, the time from each request is added together. - 'namelookup_time_us' => $httpClientResponseInfo['namelookup_time_us'], + 'namelookup_time' => $httpClientResponseInfo['namelookup_time'], // total time in seconds from the start until the connection to the remote host (or proxy) was completed in MICROSECONDS // https://curl.se/libcurl/c/CURLINFO_CONNECT_TIME.html // When a redirect is followed, the time from each request is added together. - 'connect_time_us' => $httpClientResponseInfo['connect_time_us'], + 'connect_time' => $httpClientResponseInfo['connect_time'], // time until the SSL/SSH handshake is completed in MICROSECONDS // https://curl.se/libcurl/c/CURLINFO_APPCONNECT_TIME.html @@ -41,7 +41,7 @@ public function __construct(array $httpClientResponseInfo) // This time is most often close to the CURLINFO_PRETRANSFER_TIME time, except for cases such as HTTP pipelining // where the pretransfer time can be delayed due to waits in line for the pipeline and more. // When a redirect is followed, the time from each request is added together. - 'appconnect_time_us' => $httpClientResponseInfo['appconnect_time_us'], + 'appconnect_time' => $httpClientResponseInfo['appconnect_time'] ?? null, // time until the file transfer start in MICROSECONDS // https://curl.se/libcurl/c/CURLINFO_PRETRANSFER_TIME.html @@ -49,28 +49,28 @@ public function __construct(array $httpClientResponseInfo) // This time-stamp includes all pre-transfer commands and negotiations that are specific to the particular // protocol(s) involved. It includes the sending of the protocol- specific protocol instructions that triggers a transfer. // When a redirect is followed, the time from each request is added together. - 'pretransfer_time_us' => $httpClientResponseInfo['pretransfer_time_us'], + 'pretransfer_time' => $httpClientResponseInfo['pretransfer_time'], // time for all redirection steps in MICROSECONDS // https://curl.se/libcurl/c/CURLINFO_REDIRECT_TIME.html // it took for all redirection steps include name lookup, connect, pretransfer and transfer before // final transaction was started. // CURLINFO_REDIRECT_TIME contains the complete execution time for multiple redirections. - 'redirect_time_us' => $httpClientResponseInfo['redirect_time_us'], + 'redirect_time' => $httpClientResponseInfo['redirect_time'], // time until the first byte is received in MICROSECONDS // it took from the start until the first byte is received by libcurl // https://curl.se/libcurl/c/CURLINFO_STARTTRANSFER_TIME.html // This includes CURLINFO_PRETRANSFER_TIME and also the time the server needs to calculate the result. // When a redirect is followed, the time from each request is added together. - 'starttransfer_time_us' => $httpClientResponseInfo['starttransfer_time_us'], + 'starttransfer_time' => $httpClientResponseInfo['starttransfer_time'], // total time of previous transfer in MICROSECONDS // https://curl.se/libcurl/c/CURLINFO_TOTAL_TIME.html // total time in seconds for the previous transfer, including name resolving, TCP connect etc. // The double represents the time in seconds, including fractions. // When a redirect is followed, the time from each request is added together. - 'total_time_us' => $httpClientResponseInfo['total_time_us'], + 'total_time' => $httpClientResponseInfo['total_time'], ]; } diff --git a/tests/Unit/Services/TestPerson/PersonTest.php b/tests/Unit/Services/TestPerson/PersonTest.php index 0027610a..015ebe75 100644 --- a/tests/Unit/Services/TestPerson/PersonTest.php +++ b/tests/Unit/Services/TestPerson/PersonTest.php @@ -3,11 +3,17 @@ namespace Bitrix24\SDK\Tests\Unit\Services\TestPerson; +use Money\Currency; use PHPUnit\Framework\TestCase; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Encoder\XmlEncoder; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Serializer; +use Money\Money; +use Money\Currencies\ISOCurrencies; +use Money\Formatter\AggregateMoneyFormatter; +use Money\Formatter\IntlMoneyFormatter; +use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; class PersonTest extends TestCase @@ -27,6 +33,7 @@ public function SerializerTest():void{ $person->setLastName('Khramov'); + $jsonContent = $serializer->serialize($person, 'json'); @@ -55,4 +62,30 @@ public function DeserializeTest():void{ var_dump($person); self::assertNotEmpty($person); } + + /** + * @test + * @throws \Symfony\Component\Serializer\Exception\ExceptionInterface + */ + public function NormalizeMoneyTest():void + { + $encoders = [new XmlEncoder(), new JsonEncoder()]; + $normalizers = [new ObjectNormalizer()]; + + $serializer = new Serializer($normalizers, $encoders); + + $dollars = new Money(100,new Currency('USD')); + + $numberFormatter = new \NumberFormatter('en_US', \NumberFormatter::CURRENCY); + $intlFormatter = new IntlMoneyFormatter($numberFormatter, new ISOCurrencies()); + + $moneyFormatter = new AggregateMoneyFormatter([ + 'USD' => $intlFormatter, + ]); + + $money = $moneyFormatter->format($dollars); + $jsonContent = $serializer->normalize($money,null, [AbstractNormalizer::ATTRIBUTES => ['amount']]); + var_dump($jsonContent); + self::assertNotEmpty($money); + } } \ No newline at end of file From 2f5b09430627c5ba7ada6a9a8e2b95d1ab80efca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Wed, 3 Aug 2022 11:57:58 +0300 Subject: [PATCH 04/94] -edit PersonTest.php -added a test to check the operation of products and transactions between themselves --- composer.json | 3 +- .../Services/MixedTest/MixedTest.php | 128 ++++++++++++++++++ tests/Unit/Services/TestPerson/PersonTest.php | 11 +- 3 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 tests/Integration/Services/MixedTest/MixedTest.php diff --git a/composer.json b/composer.json index fdbf9b3b..f34ae8dc 100644 --- a/composer.json +++ b/composer.json @@ -42,7 +42,8 @@ "phpunit/phpunit": "9.5.*", "symfony/stopwatch": "5.4.* || 6.*", "roave/security-advisories": "dev-master", - "ext-intl": "*" + "ext-intl": "*", + "ext-http": "*" }, "autoload": { "psr-4": { diff --git a/tests/Integration/Services/MixedTest/MixedTest.php b/tests/Integration/Services/MixedTest/MixedTest.php new file mode 100644 index 00000000..3f0d3c1f --- /dev/null +++ b/tests/Integration/Services/MixedTest/MixedTest.php @@ -0,0 +1,128 @@ + array( + 'NAME' => '1С-Битрикс: Управление сайтом - Старт', + 'CURRENCY_ID' => 'RUB', + 'PRICE' => 4900, + 'SORT' => 4 + ) + ); + $queryDataForProduct1 = http_build_query($dataProduct1, 'https://b24-5p29et.bitrix24.ru/rest/1/113n4wi2ocu6rme0/crm.product.add') . PHP_EOL; + $resQueryForProduct1 = file_get_contents("https://b24-5p29et.bitrix24.ru/rest/1/113n4wi2ocu6rme0/crm.product.add?" . $queryDataForProduct1); + $decodeResQueryForProduct1 = json_decode($resQueryForProduct1, true, 512, JSON_THROW_ON_ERROR); + $idForProduct1 = $decodeResQueryForProduct1['result']; + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_URL => $resQueryForProduct1, + )); + $response = curl_exec($curl); + curl_close($curl); + + //Создание продукта 2 + $dataProduct2 = array( + 'fields' => array( + 'NAME' => '1С-Битрикс: Управление сайтом - Старт 2 ', + 'CURRENCY_ID' => 'USD', + 'PRICE' => 4500, + 'SORT' => 1, + ) + ); + $queryDataForProduct2 = http_build_query($dataProduct2, 'https://b24-5p29et.bitrix24.ru/rest/1/113n4wi2ocu6rme0/crm.product.add') . PHP_EOL; + $resQueryForProduct2 = file_get_contents("https://b24-5p29et.bitrix24.ru/rest/1/113n4wi2ocu6rme0/crm.product.add?" . $queryDataForProduct2); + $decodeResQueryForProduct2 = json_decode($resQueryForProduct2, true, 512, JSON_THROW_ON_ERROR); + $idForProduct2 = $decodeResQueryForProduct2['result']; + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_URL => $resQueryForProduct2, + )); + $response = curl_exec($curl); + curl_close($curl); + + //Создание Сделки + $dataDeal1 = array( + 'fields' => array( + 'TITLE' => 'test deal', + 'CURRENCY_ID' => 'USD', + 'OPPORTUNITY' => 500 + ) + ); + $queryDataForDeal1 = http_build_query($dataDeal1, 'https://b24-5p29et.bitrix24.ru/rest/1/113n4wi2ocu6rme0/crm.deal.add') . PHP_EOL; + $resQueryForDeal1 = file_get_contents("https://b24-5p29et.bitrix24.ru/rest/1/113n4wi2ocu6rme0/crm.deal.add?" . $queryDataForProduct1); + $decodeResQueryForDeal1 = json_decode($resQueryForDeal1, true, 512, JSON_THROW_ON_ERROR); + $idForDeal1 = $decodeResQueryForDeal1['result']; + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_URL => $resQueryForDeal1, + )); + $response = curl_exec($curl); + curl_close($curl); + + //Добавление продукта в сделку + $addProductInDeal = array( + 'id' => $idForDeal1, + 'rows' => array( + [ + 'PRODUCT_ID' => $idForProduct1, + 'PRICE' => 100, + ], + [ + 'PRODUCT_ID' => $idForProduct2, + 'PRICE' => 100, + ] + ) + ); + $queryDataForProductInDeal = http_build_query($addProductInDeal, 'https://b24-5p29et.bitrix24.ru/rest/1/113n4wi2ocu6rme0/crm.deal.productrows.set') . PHP_EOL; + $resQueryForProductInDeal = file_get_contents("https://b24-5p29et.bitrix24.ru/rest/1/113n4wi2ocu6rme0/crm.deal.productrows.set?" . $queryDataForProductInDeal); + $decodeResQueryForDeal1 = json_decode($resQueryForProductInDeal, true, 512, JSON_THROW_ON_ERROR); + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_URL => $resQueryForProductInDeal, + )); + $response = curl_exec($curl); + curl_close($curl); + + self::assertNotEmpty($dataProduct1); + self::assertNotEmpty($dataProduct2); + self::assertNotEmpty($dataDeal1); + self::assertNotEmpty($addProductInDeal); + } + + + /** + * @throws \Bitrix24\SDK\Core\Exceptions\InvalidArgumentException + */ + public function setUp(): void + { + $this->productService = Fabric::getServiceBuilder()->getCRMScope()->product(); + $this->dealServices = Fabric::getServiceBuilder()->getCRMScope()->deal(); + } +} diff --git a/tests/Unit/Services/TestPerson/PersonTest.php b/tests/Unit/Services/TestPerson/PersonTest.php index 015ebe75..02154645 100644 --- a/tests/Unit/Services/TestPerson/PersonTest.php +++ b/tests/Unit/Services/TestPerson/PersonTest.php @@ -3,6 +3,8 @@ namespace Bitrix24\SDK\Tests\Unit\Services\TestPerson; +use Bitrix24\SDK\Services\CRM\Deal\Service\Deal; +use Bitrix24\SDK\Services\CRM\Product\Service\Product; use Money\Currency; use PHPUnit\Framework\TestCase; use Symfony\Component\Serializer\Encoder\JsonEncoder; @@ -18,6 +20,8 @@ class PersonTest extends TestCase { + protected Deal $deal; + protected Product $product; /** * @test */ @@ -74,7 +78,7 @@ public function NormalizeMoneyTest():void $serializer = new Serializer($normalizers, $encoders); - $dollars = new Money(100,new Currency('USD')); + $dollars = new Money(100, new Currency('USD')); $numberFormatter = new \NumberFormatter('en_US', \NumberFormatter::CURRENCY); $intlFormatter = new IntlMoneyFormatter($numberFormatter, new ISOCurrencies()); @@ -83,9 +87,10 @@ public function NormalizeMoneyTest():void 'USD' => $intlFormatter, ]); - $money = $moneyFormatter->format($dollars); - $jsonContent = $serializer->normalize($money,null, [AbstractNormalizer::ATTRIBUTES => ['amount']]); + $money = $moneyFormatter->format($dollars); + $jsonContent = $serializer->normalize($money, null, [AbstractNormalizer::ATTRIBUTES => ['amount']]); var_dump($jsonContent); self::assertNotEmpty($money); } + } \ No newline at end of file From 179a2ac7036a67752b91b5c71050c0751a873c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Wed, 3 Aug 2022 16:59:37 +0300 Subject: [PATCH 05/94] -add custom Normalizer(not work) --- .../Services/TestPerson/PersonNormalizer.php | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 tests/Unit/Services/TestPerson/PersonNormalizer.php diff --git a/tests/Unit/Services/TestPerson/PersonNormalizer.php b/tests/Unit/Services/TestPerson/PersonNormalizer.php new file mode 100644 index 00000000..8368beb0 --- /dev/null +++ b/tests/Unit/Services/TestPerson/PersonNormalizer.php @@ -0,0 +1,39 @@ +router = $router; + $this->normalizer = $normalizer; + } + + public function normalize($person, string $format = null, array $context = []) + { + $data = $this->normalizer->normalize($person, $format, $context); + var_dump($data); + // Здесь, добавьте, измените или удалите некоторые данные: + return $this->router->generate('topic_show', [ + 'name' => $person->getName(), + ], UrlGeneratorInterface::ABSOLUTE_URL); + } + + public function supportsNormalization($data, string $format = null, array $context = []) + { + return $data instanceof Person; + } + +} \ No newline at end of file From cce3d06747b34855ca18cd0e445260240cd4a32d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Thu, 4 Aug 2022 14:30:25 +0300 Subject: [PATCH 06/94] -edit MixedTest.php - the test works with the help of services --- composer.json | 3 +- .../Services/MixedTest/MixedTest.php | 115 ++++++------------ 2 files changed, 40 insertions(+), 78 deletions(-) diff --git a/composer.json b/composer.json index f34ae8dc..fdbf9b3b 100644 --- a/composer.json +++ b/composer.json @@ -42,8 +42,7 @@ "phpunit/phpunit": "9.5.*", "symfony/stopwatch": "5.4.* || 6.*", "roave/security-advisories": "dev-master", - "ext-intl": "*", - "ext-http": "*" + "ext-intl": "*" }, "autoload": { "psr-4": { diff --git a/tests/Integration/Services/MixedTest/MixedTest.php b/tests/Integration/Services/MixedTest/MixedTest.php index 3f0d3c1f..fb7bff7b 100644 --- a/tests/Integration/Services/MixedTest/MixedTest.php +++ b/tests/Integration/Services/MixedTest/MixedTest.php @@ -5,115 +5,77 @@ namespace Bitrix24\SDK\Tests\Integration\Services\MixedTest; use Bitrix24\SDK\Services\CRM\Deal\Service\Deal; +use Bitrix24\SDK\Services\CRM\Deal\Service\DealProductRows; use Bitrix24\SDK\Services\CRM\Product\Service\Product; use Monolog\Test\TestCase; use Bitrix24\SDK\Tests\Integration\Fabric; +use Bitrix24\SDK\Core\Exceptions\TransportException; use http; use Bitrix24\SDK\Core\Exceptions\BaseException; -use Bitrix24\SDK\Core\Exceptions\TransportException; + class MixedTest extends TestCase { protected Deal $dealServices; protected Product $productService; + protected DealProductRows $dealProductRows; /** * @test - * @throws \JsonException + * @throws BaseException + * @throws TransportException */ public function DealWithProductsTest(): void { //Создание продукта 1 $dataProduct1 = array( - 'fields' => array( - 'NAME' => '1С-Битрикс: Управление сайтом - Старт', + + 'NAME' => 'wine', 'CURRENCY_ID' => 'RUB', - 'PRICE' => 4900, - 'SORT' => 4 - ) + 'PRICE' => 475, + 'SORT' => 1 + ); - $queryDataForProduct1 = http_build_query($dataProduct1, 'https://b24-5p29et.bitrix24.ru/rest/1/113n4wi2ocu6rme0/crm.product.add') . PHP_EOL; - $resQueryForProduct1 = file_get_contents("https://b24-5p29et.bitrix24.ru/rest/1/113n4wi2ocu6rme0/crm.product.add?" . $queryDataForProduct1); - $decodeResQueryForProduct1 = json_decode($resQueryForProduct1, true, 512, JSON_THROW_ON_ERROR); - $idForProduct1 = $decodeResQueryForProduct1['result']; - $curl = curl_init(); - curl_setopt_array($curl, array( - CURLOPT_RETURNTRANSFER => 1, - CURLOPT_URL => $resQueryForProduct1, - )); - $response = curl_exec($curl); - curl_close($curl); + $productId1 = $this->productService->add($dataProduct1)->getId(); //Создание продукта 2 $dataProduct2 = array( - 'fields' => array( - 'NAME' => '1С-Битрикс: Управление сайтом - Старт 2 ', - 'CURRENCY_ID' => 'USD', - 'PRICE' => 4500, - 'SORT' => 1, - ) + + 'NAME' => 'vodka', + 'CURRENCY_ID' => 'RUB', + 'PRICE' => 675, + 'SORT' => 4 + ); - $queryDataForProduct2 = http_build_query($dataProduct2, 'https://b24-5p29et.bitrix24.ru/rest/1/113n4wi2ocu6rme0/crm.product.add') . PHP_EOL; - $resQueryForProduct2 = file_get_contents("https://b24-5p29et.bitrix24.ru/rest/1/113n4wi2ocu6rme0/crm.product.add?" . $queryDataForProduct2); - $decodeResQueryForProduct2 = json_decode($resQueryForProduct2, true, 512, JSON_THROW_ON_ERROR); - $idForProduct2 = $decodeResQueryForProduct2['result']; - $curl = curl_init(); - curl_setopt_array($curl, array( - CURLOPT_RETURNTRANSFER => 1, - CURLOPT_URL => $resQueryForProduct2, - )); - $response = curl_exec($curl); - curl_close($curl); + $productId2 = $this->productService->add($dataProduct2)->getId(); + //Создание Сделки $dataDeal1 = array( - 'fields' => array( - 'TITLE' => 'test deal', - 'CURRENCY_ID' => 'USD', - 'OPPORTUNITY' => 500 - ) + 'TITLE' => 'sale of alcohol', + 'CURRENCY_ID' => 'RUB', ); - $queryDataForDeal1 = http_build_query($dataDeal1, 'https://b24-5p29et.bitrix24.ru/rest/1/113n4wi2ocu6rme0/crm.deal.add') . PHP_EOL; - $resQueryForDeal1 = file_get_contents("https://b24-5p29et.bitrix24.ru/rest/1/113n4wi2ocu6rme0/crm.deal.add?" . $queryDataForProduct1); - $decodeResQueryForDeal1 = json_decode($resQueryForDeal1, true, 512, JSON_THROW_ON_ERROR); - $idForDeal1 = $decodeResQueryForDeal1['result']; - $curl = curl_init(); - curl_setopt_array($curl, array( - CURLOPT_RETURNTRANSFER => 1, - CURLOPT_URL => $resQueryForDeal1, - )); - $response = curl_exec($curl); - curl_close($curl); - - //Добавление продукта в сделку - $addProductInDeal = array( - 'id' => $idForDeal1, - 'rows' => array( + $dealId1 = $this->dealServices->add($dataDeal1)->getId(); + + $productsForDeal = array( + [ - 'PRODUCT_ID' => $idForProduct1, - 'PRICE' => 100, + 'PRODUCT_ID' => $productId1, + 'PRICE' => 400, + 'QUANTITY' => 1 ], [ - 'PRODUCT_ID' => $idForProduct2, - 'PRICE' => 100, + 'PRODUCT_ID' => $productId2, + 'PRICE' => 500, + 'QUANTITY' => 1 ] - ) + ); - $queryDataForProductInDeal = http_build_query($addProductInDeal, 'https://b24-5p29et.bitrix24.ru/rest/1/113n4wi2ocu6rme0/crm.deal.productrows.set') . PHP_EOL; - $resQueryForProductInDeal = file_get_contents("https://b24-5p29et.bitrix24.ru/rest/1/113n4wi2ocu6rme0/crm.deal.productrows.set?" . $queryDataForProductInDeal); - $decodeResQueryForDeal1 = json_decode($resQueryForProductInDeal, true, 512, JSON_THROW_ON_ERROR); - $curl = curl_init(); - curl_setopt_array($curl, array( - CURLOPT_RETURNTRANSFER => 1, - CURLOPT_URL => $resQueryForProductInDeal, - )); - $response = curl_exec($curl); - curl_close($curl); - - self::assertNotEmpty($dataProduct1); - self::assertNotEmpty($dataProduct2); - self::assertNotEmpty($dataDeal1); - self::assertNotEmpty($addProductInDeal); + + $addProductInDeal = $this->dealProductRows->set($dealId1, $productsForDeal); + + + self::assertGreaterThanOrEqual(1, $productId1); } @@ -124,5 +86,6 @@ public function setUp(): void { $this->productService = Fabric::getServiceBuilder()->getCRMScope()->product(); $this->dealServices = Fabric::getServiceBuilder()->getCRMScope()->deal(); + $this->dealProductRows = Fabric::getServiceBuilder()->getCRMScope()->dealProductRows(); } } From 4feb8b4f14553585f4802dbce518bb6e50f6754c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Thu, 4 Aug 2022 17:15:26 +0300 Subject: [PATCH 07/94] -update test --- .../Integration/Services/MixedTest/MixedTest.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/Integration/Services/MixedTest/MixedTest.php b/tests/Integration/Services/MixedTest/MixedTest.php index fb7bff7b..b84c223a 100644 --- a/tests/Integration/Services/MixedTest/MixedTest.php +++ b/tests/Integration/Services/MixedTest/MixedTest.php @@ -10,7 +10,6 @@ use Monolog\Test\TestCase; use Bitrix24\SDK\Tests\Integration\Fabric; use Bitrix24\SDK\Core\Exceptions\TransportException; -use http; use Bitrix24\SDK\Core\Exceptions\BaseException; @@ -31,8 +30,8 @@ public function DealWithProductsTest(): void $dataProduct1 = array( 'NAME' => 'wine', - 'CURRENCY_ID' => 'RUB', - 'PRICE' => 475, + 'CURRENCY_ID' => 'USD', + 'PRICE' => 100, 'SORT' => 1 ); @@ -42,8 +41,8 @@ public function DealWithProductsTest(): void $dataProduct2 = array( 'NAME' => 'vodka', - 'CURRENCY_ID' => 'RUB', - 'PRICE' => 675, + 'CURRENCY_ID' => 'USD', + 'PRICE' => 10, 'SORT' => 4 ); @@ -61,7 +60,7 @@ public function DealWithProductsTest(): void [ 'PRODUCT_ID' => $productId1, - 'PRICE' => 400, + 'PRICE' => 5000, 'QUANTITY' => 1 ], [ @@ -75,7 +74,11 @@ public function DealWithProductsTest(): void $addProductInDeal = $this->dealProductRows->set($dealId1, $productsForDeal); + self::assertEquals($productsForDeal[0]['PRICE'],$this->dealProductRows->get($dealId1)->getCoreResponse()->getResponseData()->getResult()->getResultData()[0]['PRICE']); + self::assertEquals($productsForDeal[1]['PRICE'],$this->dealProductRows->get($dealId1)->getCoreResponse()->getResponseData()->getResult()->getResultData()[1]['PRICE']); self::assertGreaterThanOrEqual(1, $productId1); + self::assertGreaterThanOrEqual(1, $productId2); + self::assertTrue((bool)$addProductInDeal); } From ea30810b1678b96eee72c3a6f717655fcf2bdd18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Sat, 6 Aug 2022 09:00:16 +0300 Subject: [PATCH 08/94] -edit normalizer(but I don't understand how to use it) --- .../Services/TestPerson/PersonNormalizer.php | 29 ++++++++++--------- tests/Unit/Services/TestPerson/PersonTest.php | 21 ++++++++++++++ 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/tests/Unit/Services/TestPerson/PersonNormalizer.php b/tests/Unit/Services/TestPerson/PersonNormalizer.php index 8368beb0..c8b386dc 100644 --- a/tests/Unit/Services/TestPerson/PersonNormalizer.php +++ b/tests/Unit/Services/TestPerson/PersonNormalizer.php @@ -5,35 +5,36 @@ namespace Bitrix24\SDK\Tests\Unit\Services\TestPerson; use Bitrix24\SDK\Tests\Unit\Services\TestPerson\Person; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; -class PersonNormalizer implements ContextAwareNormalizerInterface +class PersonNormalizer implements NormalizerInterface, ContextAwareNormalizerInterface { - private UrlGeneratorInterface $router; private ObjectNormalizer $normalizer; - protected Person $person; - public function __construct(UrlGeneratorInterface $router, ObjectNormalizer $normalizer) + public function __construct( ObjectNormalizer $normalizer) { - $this->router = $router; $this->normalizer = $normalizer; } - public function normalize($person, string $format = null, array $context = []) + /** + * @param Person $object + * @throws \Symfony\Component\Serializer\Exception\ExceptionInterface + */ + public function normalize($object, string $format = null, array $context = []):array { - $data = $this->normalizer->normalize($person, $format, $context); - var_dump($data); - // Здесь, добавьте, измените или удалите некоторые данные: - return $this->router->generate('topic_show', [ - 'name' => $person->getName(), - ], UrlGeneratorInterface::ABSOLUTE_URL); + $data = $this->normalizer->normalize($object,$format,$context); + return [ + 'name' => 'new_name', + 'lastname' => 'new_lastname', + ]; } - public function supportsNormalization($data, string $format = null, array $context = []) + public function supportsNormalization($data, string $format = null, array $context = []):bool { return $data instanceof Person; } + } \ No newline at end of file diff --git a/tests/Unit/Services/TestPerson/PersonTest.php b/tests/Unit/Services/TestPerson/PersonTest.php index 02154645..5d0b354f 100644 --- a/tests/Unit/Services/TestPerson/PersonTest.php +++ b/tests/Unit/Services/TestPerson/PersonTest.php @@ -16,6 +16,7 @@ use Money\Formatter\AggregateMoneyFormatter; use Money\Formatter\IntlMoneyFormatter; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; +use Bitrix24\SDK\Tests\Unit\Services\TestPerson\PersonNormalizer; class PersonTest extends TestCase @@ -67,6 +68,26 @@ public function DeserializeTest():void{ self::assertNotEmpty($person); } + /** + *@test + */ + public function NormalizePersonTest():void + { + $person = new Person(); + $encoders = [new XmlEncoder(), new JsonEncoder()]; + $normalizers = [new PersonNormalizer()]; + + $serializer = new Serializer($normalizers, $encoders); + + $person->setName('Kirill'); + $person->setLastName('Khramov'); + + $jsonContent = $serializer->serialize($person, 'json'); + self::assertNotEmpty($jsonContent); + echo $jsonContent; + + } + /** * @test * @throws \Symfony\Component\Serializer\Exception\ExceptionInterface From 568a508c4beda210adf0ec1824c5e54a327ec4b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Wed, 10 Aug 2022 18:24:47 +0300 Subject: [PATCH 09/94] -add money result in AbstactCrmItem (not work) --- .../CRM/Common/Result/AbstractCrmItem.php | 5 ++-- .../Deal/Result/DealProductRowItemResult.php | 26 +++++++++++-------- .../Deal/Result/DealProductRowItemsResult.php | 10 +++++++ .../CRM/Deal/Service/DealProductRows.php | 4 ++- .../CRM/Deal/Service/DealProductRowsTest.php | 8 ++++++ 5 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/Services/CRM/Common/Result/AbstractCrmItem.php b/src/Services/CRM/Common/Result/AbstractCrmItem.php index 0737315f..71d7b341 100644 --- a/src/Services/CRM/Common/Result/AbstractCrmItem.php +++ b/src/Services/CRM/Common/Result/AbstractCrmItem.php @@ -7,6 +7,7 @@ use Bitrix24\SDK\Core\Result\AbstractItem; use Bitrix24\SDK\Services\CRM\Userfield\Exceptions\UserfieldNotFoundException; use DateTimeImmutable; +use Money\Money; class AbstractCrmItem extends AbstractItem { @@ -30,6 +31,8 @@ public function __get($offset) case 'LEAD_ID': case 'CONTACT_ID': case 'QUOTE_ID': + // productRow + case 'OWNER_ID': if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { return (int)$this->data[$offset]; } @@ -39,9 +42,7 @@ public function __get($offset) if ($this->data[$offset] !== '' && $this->data[$offset] !== null && $this->data[$offset] !== '0') { return (int)$this->data[$offset]; } - return null; - // contact case 'EXPORT': case 'HAS_PHONE': diff --git a/src/Services/CRM/Deal/Result/DealProductRowItemResult.php b/src/Services/CRM/Deal/Result/DealProductRowItemResult.php index 5632778b..e0ddb29a 100644 --- a/src/Services/CRM/Deal/Result/DealProductRowItemResult.php +++ b/src/Services/CRM/Deal/Result/DealProductRowItemResult.php @@ -5,30 +5,34 @@ namespace Bitrix24\SDK\Services\CRM\Deal\Result; use Bitrix24\SDK\Core\Result\AbstractItem; +use Bitrix24\SDK\Services\CRM\Common\Result\AbstractCrmItem; +use Money\Money; /** * Class DealProductRowItemResult * - * @property-read int $ID - * @property-read int $OWNER_ID + * @property-read int $ID + * @property-read int $OWNER_ID * @property-read string $OWNER_TYPE - * @property-read int $PRODUCT_ID + * @property-read int $PRODUCT_ID * @property-read string $PRODUCT_NAME - * @property-read string $PRICE - * @property-read string $PRICE_EXCLUSIVE - * @property-read string $PRICE_NETTO - * @property-read string $PRICE_BRUTTO + * @property-read Money $PRICE + * @property-read Money $PRICE_EXCLUSIVE + * @property-read Money $PRICE_NETTO + * @property-read Money $PRICE_BRUTTO * @property-read string $QUANTITY - * @property-read int $DISCOUNT_TYPE_ID + * @property-read int $DISCOUNT_TYPE_ID * @property-read string $DISCOUNT_RATE * @property-read string $DISCOUNT_SUM * @property-read string $TAX_RATE * @property-read string $TAX_INCLUDED * @property-read string $CUSTOMIZED - * @property-read int $MEASURE_CODE + * @property-read int $MEASURE_CODE * @property-read string $MEASURE_NAME - * @property-read int $SORT + * @property-read int $RESERVE_ID + * @property-read int $RESERVE_QUANTITY + * @property-read int $SORT */ -class DealProductRowItemResult extends AbstractItem +class DealProductRowItemResult extends AbstractCrmItem { } \ No newline at end of file diff --git a/src/Services/CRM/Deal/Result/DealProductRowItemsResult.php b/src/Services/CRM/Deal/Result/DealProductRowItemsResult.php index 7104799b..125295a5 100644 --- a/src/Services/CRM/Deal/Result/DealProductRowItemsResult.php +++ b/src/Services/CRM/Deal/Result/DealProductRowItemsResult.php @@ -6,7 +6,9 @@ namespace Bitrix24\SDK\Services\CRM\Deal\Result; use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Response\Response; use Bitrix24\SDK\Core\Result\AbstractResult; +use Money\Currency; /** * Class DealProductRowItemsResult @@ -15,6 +17,14 @@ */ class DealProductRowItemsResult extends AbstractResult { + private Currency $currency; + + public function __construct(Response $coreResponse,Currency $currency) + { + parent::__construct($coreResponse); + $this->currency = $currency; + } + /** * @return DealProductRowItemResult[] * @throws BaseException diff --git a/src/Services/CRM/Deal/Service/DealProductRows.php b/src/Services/CRM/Deal/Service/DealProductRows.php index 3704bd20..c3867819 100644 --- a/src/Services/CRM/Deal/Service/DealProductRows.php +++ b/src/Services/CRM/Deal/Service/DealProductRows.php @@ -9,6 +9,7 @@ use Bitrix24\SDK\Core\Result\UpdatedItemResult; use Bitrix24\SDK\Services\AbstractService; use Bitrix24\SDK\Services\CRM\Deal\Result\DealProductRowItemsResult; +use Money\Currency; /** * Class DealProductRows @@ -36,7 +37,8 @@ public function get(int $dealId): DealProductRowItemsResult [ 'id' => $dealId, ] - ) + ), + new Currency('RUB') ); } diff --git a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php index 0b45ad41..f9afb1c7 100644 --- a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php +++ b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php @@ -9,6 +9,7 @@ use Bitrix24\SDK\Services\CRM\Deal\Service\Deal; use Bitrix24\SDK\Services\CRM\Deal\Service\DealProductRows; use Bitrix24\SDK\Tests\Integration\Fabric; +use Money\Money; use PHPUnit\Framework\TestCase; /** @@ -28,6 +29,7 @@ class DealProductRowsTest extends TestCase */ public function testSet(): void { + $newDealId = $this->dealService->add(['TITLE' => 'test deal'])->getId(); $this::assertCount(0, $this->dealProductRowsService->get($newDealId)->getProductRows()); $this::assertTrue( @@ -41,6 +43,12 @@ public function testSet(): void )->isSuccess() ); $this::assertCount(1, $this->dealProductRowsService->get($newDealId)->getProductRows()); + $mas = $this->dealProductRowsService->get($newDealId)->getProductRows()[0]; + var_dump($mas); + var_dump($mas->ID); + var_dump($mas->OWNER_ID); + var_dump($mas->QUANTITY); + var_dump($mas->PRICE); } /** From 385de8e9e0a0a4c90a35279703e65c35a4c94630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Sat, 13 Aug 2022 09:21:54 +0300 Subject: [PATCH 10/94] -add money result in AbstactCrmItem (work?) -edit test --- composer.json | 6 +++--- src/Services/CRM/Common/Result/AbstractCrmItem.php | 6 ++++++ .../CRM/Deal/Service/DealProductRowsTest.php | 14 ++++++++++---- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index fdbf9b3b..7485fa08 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,8 @@ "moneyphp/money": "3.* || 4.*", "symfony/serializer": "6.*||^6.1", "symfony/routing": "^4.4|^5.3|^6.0", - "symfony/property-access": "6.* || 6.1.*" + "symfony/property-access": "6.* || 6.1.*", + "ext-intl": "*" }, "require-dev": { "monolog/monolog": "2.1.*", @@ -41,8 +42,7 @@ "phpstan/phpstan": "1.*", "phpunit/phpunit": "9.5.*", "symfony/stopwatch": "5.4.* || 6.*", - "roave/security-advisories": "dev-master", - "ext-intl": "*" + "roave/security-advisories": "dev-master" }, "autoload": { "psr-4": { diff --git a/src/Services/CRM/Common/Result/AbstractCrmItem.php b/src/Services/CRM/Common/Result/AbstractCrmItem.php index 71d7b341..6be40bfd 100644 --- a/src/Services/CRM/Common/Result/AbstractCrmItem.php +++ b/src/Services/CRM/Common/Result/AbstractCrmItem.php @@ -7,6 +7,7 @@ use Bitrix24\SDK\Core\Result\AbstractItem; use Bitrix24\SDK\Services\CRM\Userfield\Exceptions\UserfieldNotFoundException; use DateTimeImmutable; +use Money\Currency; use Money\Money; class AbstractCrmItem extends AbstractItem @@ -50,6 +51,11 @@ public function __get($offset) case 'HAS_IMOL': case 'OPENED': // deal + case 'PRICE': + if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { + return new Money($this->data[$offset],new Currency('RUB')); + } + return null; case 'IS_MANUAL_OPPORTUNITY': case 'CLOSED': case 'IS_NEW': diff --git a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php index f9afb1c7..bba2891b 100644 --- a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php +++ b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php @@ -9,6 +9,9 @@ use Bitrix24\SDK\Services\CRM\Deal\Service\Deal; use Bitrix24\SDK\Services\CRM\Deal\Service\DealProductRows; use Bitrix24\SDK\Tests\Integration\Fabric; +use Money\Currencies\ISOCurrencies; +use Money\Currency; +use Money\Formatter\DecimalMoneyFormatter; use Money\Money; use PHPUnit\Framework\TestCase; @@ -30,6 +33,10 @@ class DealProductRowsTest extends TestCase public function testSet(): void { + $callCosts = new Money(1000, new Currency('RUB')); + $currencies = new ISOCurrencies(); + + $moneyFormatter = new DecimalMoneyFormatter($currencies); $newDealId = $this->dealService->add(['TITLE' => 'test deal'])->getId(); $this::assertCount(0, $this->dealProductRowsService->get($newDealId)->getProductRows()); $this::assertTrue( @@ -38,6 +45,7 @@ public function testSet(): void [ [ 'PRODUCT_NAME' => 'qqqq', + 'PRICE'=> $moneyFormatter->format($callCosts), ], ] )->isSuccess() @@ -45,10 +53,8 @@ public function testSet(): void $this::assertCount(1, $this->dealProductRowsService->get($newDealId)->getProductRows()); $mas = $this->dealProductRowsService->get($newDealId)->getProductRows()[0]; var_dump($mas); - var_dump($mas->ID); - var_dump($mas->OWNER_ID); - var_dump($mas->QUANTITY); - var_dump($mas->PRICE); + $price = $mas->PRICE; + var_dump($price); } /** From 67b0b486f4fb2be6ea9f4680e2e9672bcfb97a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Wed, 17 Aug 2022 15:04:42 +0300 Subject: [PATCH 11/94] -edit money result in AbstactCrmItem (work?) -edit test --- src/Services/CRM/Common/Result/AbstractCrmItem.php | 4 +++- .../Services/CRM/Deal/Service/DealProductRowsTest.php | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Services/CRM/Common/Result/AbstractCrmItem.php b/src/Services/CRM/Common/Result/AbstractCrmItem.php index 6be40bfd..d2997b5a 100644 --- a/src/Services/CRM/Common/Result/AbstractCrmItem.php +++ b/src/Services/CRM/Common/Result/AbstractCrmItem.php @@ -52,8 +52,10 @@ public function __get($offset) case 'OPENED': // deal case 'PRICE': + if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { - return new Money($this->data[$offset],new Currency('RUB')); + $var = $this->data[$offset] * 100; + return new Money((string)$var,new Currency('RUB')); } return null; case 'IS_MANUAL_OPPORTUNITY': diff --git a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php index bba2891b..35696eca 100644 --- a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php +++ b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php @@ -33,7 +33,7 @@ class DealProductRowsTest extends TestCase public function testSet(): void { - $callCosts = new Money(1000, new Currency('RUB')); + $callCosts = new Money(1050, new Currency('RUB')); $currencies = new ISOCurrencies(); $moneyFormatter = new DecimalMoneyFormatter($currencies); @@ -52,9 +52,9 @@ public function testSet(): void ); $this::assertCount(1, $this->dealProductRowsService->get($newDealId)->getProductRows()); $mas = $this->dealProductRowsService->get($newDealId)->getProductRows()[0]; - var_dump($mas); - $price = $mas->PRICE; - var_dump($price); + $money = $moneyFormatter->format($mas->PRICE); + + } /** From ea8484193af3c9b165af6b4a80792b7e7c208a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Fri, 19 Aug 2022 18:50:06 +0300 Subject: [PATCH 12/94] -edit test -add improvements to the method DEAL_PRODUCT_ROW so that it can use the currency --- .../CRM/Common/Result/AbstractCrmItem.php | 6 ++- .../Deal/Result/DealProductRowItemResult.php | 26 ++++++++++++- .../Deal/Result/DealProductRowItemsResult.php | 2 +- .../CRM/Deal/Service/DealProductRows.php | 39 ++++++++++++++++++- .../CRM/Deal/Service/DealProductRowsTest.php | 13 +++++-- 5 files changed, 78 insertions(+), 8 deletions(-) diff --git a/src/Services/CRM/Common/Result/AbstractCrmItem.php b/src/Services/CRM/Common/Result/AbstractCrmItem.php index d2997b5a..16a8c501 100644 --- a/src/Services/CRM/Common/Result/AbstractCrmItem.php +++ b/src/Services/CRM/Common/Result/AbstractCrmItem.php @@ -21,6 +21,10 @@ class AbstractCrmItem extends AbstractItem */ public function __get($offset) { + + var_dump('Зашли в AbstractCrmItem'); + var_dump(__METHOD__); + var_dump($offset); // todo унести в отдельный класс и покрыть тестами // приведение полей к реальным типам данных для основных сущностей CRM switch ($offset) { @@ -55,7 +59,7 @@ public function __get($offset) if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { $var = $this->data[$offset] * 100; - return new Money((string)$var,new Currency('RUB')); + return new Money((string)$var,new Currency('USD')); } return null; case 'IS_MANUAL_OPPORTUNITY': diff --git a/src/Services/CRM/Deal/Result/DealProductRowItemResult.php b/src/Services/CRM/Deal/Result/DealProductRowItemResult.php index e0ddb29a..b0d7af3c 100644 --- a/src/Services/CRM/Deal/Result/DealProductRowItemResult.php +++ b/src/Services/CRM/Deal/Result/DealProductRowItemResult.php @@ -4,8 +4,8 @@ namespace Bitrix24\SDK\Services\CRM\Deal\Result; -use Bitrix24\SDK\Core\Result\AbstractItem; use Bitrix24\SDK\Services\CRM\Common\Result\AbstractCrmItem; +use Money\Currency; use Money\Money; /** @@ -35,4 +35,28 @@ */ class DealProductRowItemResult extends AbstractCrmItem { + private Currency $currency; + + /** + * @param \Money\Currency $currency + */ + public function __construct(array $data,Currency $currency) + { + parent::__construct($data); + $this->currency = $currency; + } + + public function __get($offset) + { + + var_dump('Cтрока сделки'); + var_dump(__METHOD__); + var_dump($offset); + var_dump($this->currency->getCode()); + + return parent::__get($offset); + + + } + } \ No newline at end of file diff --git a/src/Services/CRM/Deal/Result/DealProductRowItemsResult.php b/src/Services/CRM/Deal/Result/DealProductRowItemsResult.php index 125295a5..64bbbcc2 100644 --- a/src/Services/CRM/Deal/Result/DealProductRowItemsResult.php +++ b/src/Services/CRM/Deal/Result/DealProductRowItemsResult.php @@ -33,7 +33,7 @@ public function getProductRows(): array { $res = []; foreach ($this->getCoreResponse()->getResponseData()->getResult()->getResultData() as $productRow) { - $res[] = new DealProductRowItemResult($productRow); + $res[] = new DealProductRowItemResult($productRow,$this->currency); } return $res; diff --git a/src/Services/CRM/Deal/Service/DealProductRows.php b/src/Services/CRM/Deal/Service/DealProductRows.php index c3867819..40fe4976 100644 --- a/src/Services/CRM/Deal/Service/DealProductRows.php +++ b/src/Services/CRM/Deal/Service/DealProductRows.php @@ -9,6 +9,7 @@ use Bitrix24\SDK\Core\Result\UpdatedItemResult; use Bitrix24\SDK\Services\AbstractService; use Bitrix24\SDK\Services\CRM\Deal\Result\DealProductRowItemsResult; +use Bitrix24\SDK\Services\CRM\Deal\Result\DealResult; use Money\Currency; /** @@ -29,7 +30,7 @@ class DealProductRows extends AbstractService * @throws BaseException * @throws TransportException */ - public function get(int $dealId): DealProductRowItemsResult + public function getStupid(int $dealId, Currency $currency): DealProductRowItemsResult { return new DealProductRowItemsResult( $this->core->call( @@ -38,10 +39,44 @@ public function get(int $dealId): DealProductRowItemsResult 'id' => $dealId, ] ), - new Currency('RUB') + $currency ); } + public function getSmart(int $dealId): DealProductRowItemsResult + { + $deal = new DealResult($this->core->call('crm.deal.get', ['id' => $dealId])); + $currency = new Currency($deal->deal()->CURRENCY_ID); + return new DealProductRowItemsResult( + $this->core->call( + 'crm.deal.productrows.get', + [ + 'id' => $dealId, + ] + ), + $currency + ); + } + public function getSuperSmart(int $dealId): DealProductRowItemsResult + { + /* $deal = new DealResult($this->core->call('crm.deal.get', ['id' => $dealId])); + $currency = new Currency($deal->deal()->CURRENCY_ID); + return new DealProductRowItemsResult( + $this->core->call( + 'crm.deal.productrows.get', + [ + 'id' => $dealId, + ] + ), + $currency + );*/ + // todo Получить сделку и табличную часть за один запрос к Api + } + + public function getSuperSuperSmart(){ + // todo Метод позволяет экономить один запрос если мы уже знаем валюту, а если не знаем то делает этот запрос. + } + /** * Creates or updates product entries inside the specified deal. * diff --git a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php index 35696eca..7816fa3e 100644 --- a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php +++ b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php @@ -64,19 +64,26 @@ public function testSet(): void */ public function testGet(): void { - $newDealId = $this->dealService->add(['TITLE' => 'test deal'])->getId(); - $this::assertCount(0, $this->dealProductRowsService->get($newDealId)->getProductRows()); + $callCosts = new Money(1050, new Currency('EUR')); + $currencies = new ISOCurrencies(); + + $moneyFormatter = new DecimalMoneyFormatter($currencies); + $newDealId = $this->dealService->add(['TITLE' => 'test deal','CURRENCY_ID'=>$callCosts->getCurrency()->getCode()])->getId(); $this::assertTrue( $this->dealProductRowsService->set( $newDealId, [ [ 'PRODUCT_NAME' => 'qqqq', + 'PRICE'=> $moneyFormatter->format($callCosts), ], ] )->isSuccess() ); - $this::assertCount(1, $this->dealProductRowsService->get($newDealId)->getProductRows()); + $res = $this->dealProductRowsService->getSmart($newDealId); + foreach ($res->getProductRows() as $productRow){ + var_dump($productRow->PRICE); + } } public function setUp(): void From 53b855f8dcf8df022061e945cccf69490cdfc4cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Wed, 24 Aug 2022 16:58:47 +0300 Subject: [PATCH 13/94] -add method getSuperSmart for DealProductRows.php method 2in1(crm.deal.get,crm.deal.productrows.get) --- .../CRM/Common/Result/AbstractCrmItem.php | 5 +-- .../Deal/Result/DealProductRowItemResult.php | 10 +---- .../CRM/Deal/Service/DealProductRows.php | 44 +++++++++++-------- .../CRM/Deal/Service/DealProductRowsTest.php | 2 +- 4 files changed, 29 insertions(+), 32 deletions(-) diff --git a/src/Services/CRM/Common/Result/AbstractCrmItem.php b/src/Services/CRM/Common/Result/AbstractCrmItem.php index 16a8c501..5f75cb10 100644 --- a/src/Services/CRM/Common/Result/AbstractCrmItem.php +++ b/src/Services/CRM/Common/Result/AbstractCrmItem.php @@ -22,9 +22,7 @@ class AbstractCrmItem extends AbstractItem public function __get($offset) { - var_dump('Зашли в AbstractCrmItem'); - var_dump(__METHOD__); - var_dump($offset); + // todo унести в отдельный класс и покрыть тестами // приведение полей к реальным типам данных для основных сущностей CRM switch ($offset) { @@ -56,7 +54,6 @@ public function __get($offset) case 'OPENED': // deal case 'PRICE': - if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { $var = $this->data[$offset] * 100; return new Money((string)$var,new Currency('USD')); diff --git a/src/Services/CRM/Deal/Result/DealProductRowItemResult.php b/src/Services/CRM/Deal/Result/DealProductRowItemResult.php index b0d7af3c..0f3f2716 100644 --- a/src/Services/CRM/Deal/Result/DealProductRowItemResult.php +++ b/src/Services/CRM/Deal/Result/DealProductRowItemResult.php @@ -38,9 +38,10 @@ class DealProductRowItemResult extends AbstractCrmItem private Currency $currency; /** + * @param array $data * @param \Money\Currency $currency */ - public function __construct(array $data,Currency $currency) + public function __construct(array $data, Currency $currency) { parent::__construct($data); $this->currency = $currency; @@ -48,15 +49,8 @@ public function __construct(array $data,Currency $currency) public function __get($offset) { - - var_dump('Cтрока сделки'); - var_dump(__METHOD__); - var_dump($offset); - var_dump($this->currency->getCode()); - return parent::__get($offset); - } } \ No newline at end of file diff --git a/src/Services/CRM/Deal/Service/DealProductRows.php b/src/Services/CRM/Deal/Service/DealProductRows.php index 40fe4976..fcfd21ab 100644 --- a/src/Services/CRM/Deal/Service/DealProductRows.php +++ b/src/Services/CRM/Deal/Service/DealProductRows.php @@ -25,10 +25,10 @@ class DealProductRows extends AbstractService * @link https://training.bitrix24.com/rest_help/crm/deals/crm_deal_productrows_get.php * * @param int $dealId - * + * @param \Money\Currency $currency * @return DealProductRowItemsResult - * @throws BaseException - * @throws TransportException + * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + * @throws \Bitrix24\SDK\Core\Exceptions\TransportException */ public function getStupid(int $dealId, Currency $currency): DealProductRowItemsResult { @@ -39,10 +39,14 @@ public function getStupid(int $dealId, Currency $currency): DealProductRowItemsR 'id' => $dealId, ] ), - $currency + $currency ); } + /** + * @throws \Bitrix24\SDK\Core\Exceptions\TransportException + * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + */ public function getSmart(int $dealId): DealProductRowItemsResult { $deal = new DealResult($this->core->call('crm.deal.get', ['id' => $dealId])); @@ -57,25 +61,27 @@ public function getSmart(int $dealId): DealProductRowItemsResult $currency ); } + + /** + * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + */ public function getSuperSmart(int $dealId): DealProductRowItemsResult { - /* $deal = new DealResult($this->core->call('crm.deal.get', ['id' => $dealId])); - $currency = new Currency($deal->deal()->CURRENCY_ID); - return new DealProductRowItemsResult( - $this->core->call( - 'crm.deal.productrows.get', - [ - 'id' => $dealId, - ] - ), - $currency - );*/ + $data = $this->core->call('batch',array( + 'halt'=>0, + 'cmd'=>array( + $deal = new DealResult($this->core->call('crm.deal.get', ['id' => $dealId])), + $dealProductRow = new DealProductRowItemsResult($this->core->call('crm.deal.productrows.get',['id' => $dealId,]),new Currency($deal->deal()->CURRENCY_ID)) + ) + )); + return $dealProductRow; // todo Получить сделку и табличную часть за один запрос к Api } - public function getSuperSuperSmart(){ - // todo Метод позволяет экономить один запрос если мы уже знаем валюту, а если не знаем то делает этот запрос. - } + public function getSuperSuperSmart() + { + // todo Метод позволяет экономить один запрос если мы уже знаем валюту, а если не знаем то делает этот запрос. + } /** * Creates or updates product entries inside the specified deal. @@ -115,7 +121,7 @@ public function set(int $dealId, array $productRows): UpdatedItemResult $this->core->call( 'crm.deal.productrows.set', [ - 'id' => $dealId, + 'id' => $dealId, 'rows' => $productRows, ] ) diff --git a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php index 7816fa3e..cf53c493 100644 --- a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php +++ b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php @@ -80,7 +80,7 @@ public function testGet(): void ] )->isSuccess() ); - $res = $this->dealProductRowsService->getSmart($newDealId); + $res = $this->dealProductRowsService->getSuperSmart($newDealId); foreach ($res->getProductRows() as $productRow){ var_dump($productRow->PRICE); } From fdcbb218a16a4f56ac75e024166a60d40d747b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Mon, 29 Aug 2022 12:50:29 +0300 Subject: [PATCH 14/94] =?UTF-8?q?-=D1=82=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20?= =?UTF-8?q?=D0=B2=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D0=B5=20AbstarctCrmItem?= =?UTF-8?q?=20=D0=BC=D0=BE=D0=B6=D0=BD=D0=BE=20=D0=BE=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D1=89=D0=B0=D1=82=D1=8C=D1=81=D1=8F=20=D0=BA=20currency,=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B8=D0=BB=D1=81=D1=8F=20=D1=8D=D1=82=D0=BE?= =?UTF-8?q?=20=D0=BF=D1=83=D1=82=D0=B5=D0=BC=20=D0=B7=D0=B0=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=BE=D0=B1=D0=BB=D0=B0=D1=81=D1=82=D0=B8=20?= =?UTF-8?q?=D0=B2=D0=B8=D0=B4=D0=B8=D0=BC=D0=BE=D1=81=D1=82=D0=B8=20=D1=81?= =?UTF-8?q?=20private=20=D0=BD=D0=B0=20protected=20=D0=B2=20DealProductRow?= =?UTF-8?q?ItemResult.php.(=D0=A2=D0=B0=D0=BA=20=D0=BC=D0=BE=D0=B6=D0=BD?= =?UTF-8?q?=D0=BE=20=D0=B4=D0=B5=D0=BB=D0=B0=D1=82=D1=8C=20=3F)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CRM/Common/Result/AbstractCrmItem.php | 4 ++- .../Deal/Result/DealProductRowItemResult.php | 3 +- .../CRM/Deal/Service/DealProductRows.php | 22 +++++++++---- .../CRM/Deal/Service/DealProductRowsTest.php | 33 ++++++++++++++++++- 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src/Services/CRM/Common/Result/AbstractCrmItem.php b/src/Services/CRM/Common/Result/AbstractCrmItem.php index 5f75cb10..abab2784 100644 --- a/src/Services/CRM/Common/Result/AbstractCrmItem.php +++ b/src/Services/CRM/Common/Result/AbstractCrmItem.php @@ -5,6 +5,7 @@ namespace Bitrix24\SDK\Services\CRM\Common\Result; use Bitrix24\SDK\Core\Result\AbstractItem; +use Bitrix24\SDK\Services\CRM\Deal\Result\DealProductRowItemsResult; use Bitrix24\SDK\Services\CRM\Userfield\Exceptions\UserfieldNotFoundException; use DateTimeImmutable; use Money\Currency; @@ -19,6 +20,7 @@ class AbstractCrmItem extends AbstractItem * * @return bool|\DateTimeImmutable|int|mixed|null */ + public function __get($offset) { @@ -56,7 +58,7 @@ public function __get($offset) case 'PRICE': if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { $var = $this->data[$offset] * 100; - return new Money((string)$var,new Currency('USD')); + return new Money((string)$var,new Currency($this->currency->getCode())); } return null; case 'IS_MANUAL_OPPORTUNITY': diff --git a/src/Services/CRM/Deal/Result/DealProductRowItemResult.php b/src/Services/CRM/Deal/Result/DealProductRowItemResult.php index 0f3f2716..540d161f 100644 --- a/src/Services/CRM/Deal/Result/DealProductRowItemResult.php +++ b/src/Services/CRM/Deal/Result/DealProductRowItemResult.php @@ -35,7 +35,7 @@ */ class DealProductRowItemResult extends AbstractCrmItem { - private Currency $currency; + protected Currency $currency; /** * @param array $data @@ -50,7 +50,6 @@ public function __construct(array $data, Currency $currency) public function __get($offset) { return parent::__get($offset); - } } \ No newline at end of file diff --git a/src/Services/CRM/Deal/Service/DealProductRows.php b/src/Services/CRM/Deal/Service/DealProductRows.php index fcfd21ab..6bbedf6d 100644 --- a/src/Services/CRM/Deal/Service/DealProductRows.php +++ b/src/Services/CRM/Deal/Service/DealProductRows.php @@ -67,14 +67,22 @@ public function getSmart(int $dealId): DealProductRowItemsResult */ public function getSuperSmart(int $dealId): DealProductRowItemsResult { - $data = $this->core->call('batch',array( + $res = $this->core->call('batch',[ 'halt'=>0, - 'cmd'=>array( - $deal = new DealResult($this->core->call('crm.deal.get', ['id' => $dealId])), - $dealProductRow = new DealProductRowItemsResult($this->core->call('crm.deal.productrows.get',['id' => $dealId,]),new Currency($deal->deal()->CURRENCY_ID)) - ) - )); - return $dealProductRow; + 'cmd'=>[ + $deal = new DealResult( $this->core->call('crm.deal.get', ['id' => $dealId])), + $rows = new DealProductRowItemsResult($this->core->call('crm.deal.productrows.get',['id' => $dealId,]),new Currency($deal->deal()->CURRENCY_ID)), + + ], + ]); + return $rows; + /*$data = $res->getResponseData()->getResult(); + $array = $data->getResultData()['result']['deal']['CURRENCY_ID']; + var_dump($array); + return new DealProductRowItemsResult( + $data->getResultData()['result']['deal']['ID'] , + new Currency($array) + );*/ // todo Получить сделку и табличную часть за один запрос к Api } diff --git a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php index cf53c493..b2c7accf 100644 --- a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php +++ b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php @@ -60,7 +60,6 @@ public function testSet(): void /** * @throws BaseException * @throws TransportException - * @covers \Bitrix24\SDK\Services\CRM\Deal\Service\DealProductRows::get */ public function testGet(): void { @@ -86,9 +85,41 @@ public function testGet(): void } } + /** + * @throws BaseException + * @throws TransportException + */ + public function testBatch():void{ + $callCosts = new Money(1050, new Currency('EUR')); + $currencies = new ISOCurrencies(); + + $moneyFormatter = new DecimalMoneyFormatter($currencies); + $newDealId = $this->dealService->add(['TITLE' => 'test deal','CURRENCY_ID'=>$callCosts->getCurrency()->getCode()])->getId(); + $this::assertTrue( + $this->dealProductRowsService->set( + $newDealId, + [ + [ + 'PRODUCT_NAME' => 'qqqq', + 'PRICE'=> $moneyFormatter->format($callCosts), + ], + ] + )->isSuccess() + ); + $data = $this->core->call('batch',array( + 'halt'=>0, + 'cmd'=>array( + 'deal'=>'crm.deal.get?id='.$newDealId, + 'productrow'=>'crm.deal.productrows.get?ID=$result[deal]['.$newDealId.'][ID]', + ) + )); + print_r($data); + } + public function setUp(): void { $this->dealService = Fabric::getServiceBuilder()->getCRMScope()->deal(); $this->dealProductRowsService = Fabric::getServiceBuilder()->getCRMScope()->dealProductRows(); + $this->core=Fabric::getCore(); } } \ No newline at end of file From 22c4e7099df3849e2fd1a7f72c12b243639e6d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Tue, 30 Aug 2022 13:39:37 +0300 Subject: [PATCH 15/94] -add method getSuperSuperSmart --- .../CRM/Common/Result/AbstractCrmItem.php | 2 +- .../CRM/Deal/Service/DealProductRows.php | 37 ++++++++++++++++++- .../CRM/Deal/Service/DealProductRowsTest.php | 11 +++--- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/Services/CRM/Common/Result/AbstractCrmItem.php b/src/Services/CRM/Common/Result/AbstractCrmItem.php index abab2784..2d70acde 100644 --- a/src/Services/CRM/Common/Result/AbstractCrmItem.php +++ b/src/Services/CRM/Common/Result/AbstractCrmItem.php @@ -5,7 +5,6 @@ namespace Bitrix24\SDK\Services\CRM\Common\Result; use Bitrix24\SDK\Core\Result\AbstractItem; -use Bitrix24\SDK\Services\CRM\Deal\Result\DealProductRowItemsResult; use Bitrix24\SDK\Services\CRM\Userfield\Exceptions\UserfieldNotFoundException; use DateTimeImmutable; use Money\Currency; @@ -58,6 +57,7 @@ public function __get($offset) case 'PRICE': if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { $var = $this->data[$offset] * 100; + var_dump($offset); return new Money((string)$var,new Currency($this->currency->getCode())); } return null; diff --git a/src/Services/CRM/Deal/Service/DealProductRows.php b/src/Services/CRM/Deal/Service/DealProductRows.php index 6bbedf6d..9ef44840 100644 --- a/src/Services/CRM/Deal/Service/DealProductRows.php +++ b/src/Services/CRM/Deal/Service/DealProductRows.php @@ -44,8 +44,10 @@ public function getStupid(int $dealId, Currency $currency): DealProductRowItemsR } /** - * @throws \Bitrix24\SDK\Core\Exceptions\TransportException + * @param int $dealId + * @return \Bitrix24\SDK\Services\CRM\Deal\Result\DealProductRowItemsResult * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + * @throws \Bitrix24\SDK\Core\Exceptions\TransportException */ public function getSmart(int $dealId): DealProductRowItemsResult { @@ -63,7 +65,10 @@ public function getSmart(int $dealId): DealProductRowItemsResult } /** + * @param int $dealId + * @return \Bitrix24\SDK\Services\CRM\Deal\Result\DealProductRowItemsResult * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + * @throws \Bitrix24\SDK\Core\Exceptions\TransportException */ public function getSuperSmart(int $dealId): DealProductRowItemsResult { @@ -86,8 +91,36 @@ public function getSuperSmart(int $dealId): DealProductRowItemsResult // todo Получить сделку и табличную часть за один запрос к Api } - public function getSuperSuperSmart() + /** + * @param int $dealId + * @param \Money\Currency|null $currency + * @return \Bitrix24\SDK\Services\CRM\Deal\Result\DealProductRowItemsResult + * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + * @throws \Bitrix24\SDK\Core\Exceptions\TransportException + */ + public function getSuperSuperSmart(int $dealId, Currency $currency = null): DealProductRowItemsResult { + if ($currency === null){ + $res = $this->core->call('batch',[ + 'halt'=>0, + 'cmd'=>[ + $deal = new DealResult( $this->core->call('crm.deal.get', ['id' => $dealId])), + $rows = new DealProductRowItemsResult($this->core->call('crm.deal.productrows.get',['id' => $dealId,]),new Currency($deal->deal()->CURRENCY_ID)), + + ], + ]); + return $rows; + }else{ + return new DealProductRowItemsResult( + $this->core->call( + 'crm.deal.productrows.get', + [ + 'id' => $dealId, + ] + ), + $currency + ); + } // todo Метод позволяет экономить один запрос если мы уже знаем валюту, а если не знаем то делает этот запрос. } diff --git a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php index b2c7accf..3aaf26d6 100644 --- a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php +++ b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php @@ -38,7 +38,7 @@ public function testSet(): void $moneyFormatter = new DecimalMoneyFormatter($currencies); $newDealId = $this->dealService->add(['TITLE' => 'test deal'])->getId(); - $this::assertCount(0, $this->dealProductRowsService->get($newDealId)->getProductRows()); + $this::assertCount(0, $this->dealProductRowsService->getSuperSmart($newDealId)->getProductRows()); $this::assertTrue( $this->dealProductRowsService->set( $newDealId, @@ -50,8 +50,8 @@ public function testSet(): void ] )->isSuccess() ); - $this::assertCount(1, $this->dealProductRowsService->get($newDealId)->getProductRows()); - $mas = $this->dealProductRowsService->get($newDealId)->getProductRows()[0]; + $this::assertCount(1, $this->dealProductRowsService->getSuperSmart($newDealId)->getProductRows()); + $mas = $this->dealProductRowsService->getSuperSmart($newDealId)->getProductRows()[0]; $money = $moneyFormatter->format($mas->PRICE); @@ -63,7 +63,7 @@ public function testSet(): void */ public function testGet(): void { - $callCosts = new Money(1050, new Currency('EUR')); + $callCosts = new Money(1050, new Currency('USD')); $currencies = new ISOCurrencies(); $moneyFormatter = new DecimalMoneyFormatter($currencies); @@ -79,7 +79,8 @@ public function testGet(): void ] )->isSuccess() ); - $res = $this->dealProductRowsService->getSuperSmart($newDealId); + $currency = $callCosts->getCurrency(); + $res = $this->dealProductRowsService->getStupid($newDealId,$currency); foreach ($res->getProductRows() as $productRow){ var_dump($productRow->PRICE); } From e42100f5f55be5422dd7425af94718be18114048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Wed, 31 Aug 2022 13:08:25 +0300 Subject: [PATCH 16/94] -add method getSuperSuperSmart -delete var_dump --- src/Services/CRM/Common/Result/AbstractCrmItem.php | 1 - .../Services/CRM/Deal/Service/DealProductRowsTest.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Services/CRM/Common/Result/AbstractCrmItem.php b/src/Services/CRM/Common/Result/AbstractCrmItem.php index 2d70acde..638366df 100644 --- a/src/Services/CRM/Common/Result/AbstractCrmItem.php +++ b/src/Services/CRM/Common/Result/AbstractCrmItem.php @@ -57,7 +57,6 @@ public function __get($offset) case 'PRICE': if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { $var = $this->data[$offset] * 100; - var_dump($offset); return new Money((string)$var,new Currency($this->currency->getCode())); } return null; diff --git a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php index 3aaf26d6..e5e92790 100644 --- a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php +++ b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php @@ -80,7 +80,7 @@ public function testGet(): void )->isSuccess() ); $currency = $callCosts->getCurrency(); - $res = $this->dealProductRowsService->getStupid($newDealId,$currency); + $res = $this->dealProductRowsService->getSuperSuperSmart($newDealId,$currency); foreach ($res->getProductRows() as $productRow){ var_dump($productRow->PRICE); } From e1037c9df860b8571c4309a46a1d6dd5e37665d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Mon, 5 Sep 2022 20:06:53 +0300 Subject: [PATCH 17/94] - edit composer.json (-delete symfony routing) --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 7485fa08..e09b099d 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,6 @@ "ramsey/uuid": "^4.2.3", "moneyphp/money": "3.* || 4.*", "symfony/serializer": "6.*||^6.1", - "symfony/routing": "^4.4|^5.3|^6.0", "symfony/property-access": "6.* || 6.1.*", "ext-intl": "*" }, From 3c4569892b6eff8d440225effa51e712286fdac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Mon, 5 Sep 2022 20:41:29 +0300 Subject: [PATCH 18/94] - removed unnecessary files --- .../Services/MixedTest/MixedTest.php | 94 -------------- tests/Unit/Services/TestPerson/Person.php | 35 ------ .../Services/TestPerson/PersonNormalizer.php | 40 ------ tests/Unit/Services/TestPerson/PersonTest.php | 117 ------------------ 4 files changed, 286 deletions(-) delete mode 100644 tests/Integration/Services/MixedTest/MixedTest.php delete mode 100644 tests/Unit/Services/TestPerson/Person.php delete mode 100644 tests/Unit/Services/TestPerson/PersonNormalizer.php delete mode 100644 tests/Unit/Services/TestPerson/PersonTest.php diff --git a/tests/Integration/Services/MixedTest/MixedTest.php b/tests/Integration/Services/MixedTest/MixedTest.php deleted file mode 100644 index b84c223a..00000000 --- a/tests/Integration/Services/MixedTest/MixedTest.php +++ /dev/null @@ -1,94 +0,0 @@ - 'wine', - 'CURRENCY_ID' => 'USD', - 'PRICE' => 100, - 'SORT' => 1 - - ); - $productId1 = $this->productService->add($dataProduct1)->getId(); - - //Создание продукта 2 - $dataProduct2 = array( - - 'NAME' => 'vodka', - 'CURRENCY_ID' => 'USD', - 'PRICE' => 10, - 'SORT' => 4 - - ); - $productId2 = $this->productService->add($dataProduct2)->getId(); - - - //Создание Сделки - $dataDeal1 = array( - 'TITLE' => 'sale of alcohol', - 'CURRENCY_ID' => 'RUB', - ); - $dealId1 = $this->dealServices->add($dataDeal1)->getId(); - - $productsForDeal = array( - - [ - 'PRODUCT_ID' => $productId1, - 'PRICE' => 5000, - 'QUANTITY' => 1 - ], - [ - 'PRODUCT_ID' => $productId2, - 'PRICE' => 500, - 'QUANTITY' => 1 - ] - - ); - - $addProductInDeal = $this->dealProductRows->set($dealId1, $productsForDeal); - - - self::assertEquals($productsForDeal[0]['PRICE'],$this->dealProductRows->get($dealId1)->getCoreResponse()->getResponseData()->getResult()->getResultData()[0]['PRICE']); - self::assertEquals($productsForDeal[1]['PRICE'],$this->dealProductRows->get($dealId1)->getCoreResponse()->getResponseData()->getResult()->getResultData()[1]['PRICE']); - self::assertGreaterThanOrEqual(1, $productId1); - self::assertGreaterThanOrEqual(1, $productId2); - self::assertTrue((bool)$addProductInDeal); - } - - - /** - * @throws \Bitrix24\SDK\Core\Exceptions\InvalidArgumentException - */ - public function setUp(): void - { - $this->productService = Fabric::getServiceBuilder()->getCRMScope()->product(); - $this->dealServices = Fabric::getServiceBuilder()->getCRMScope()->deal(); - $this->dealProductRows = Fabric::getServiceBuilder()->getCRMScope()->dealProductRows(); - } -} diff --git a/tests/Unit/Services/TestPerson/Person.php b/tests/Unit/Services/TestPerson/Person.php deleted file mode 100644 index 41c3c19f..00000000 --- a/tests/Unit/Services/TestPerson/Person.php +++ /dev/null @@ -1,35 +0,0 @@ -name; - } - - public function getLastName() - { - return $this->lastName; - } - - - // Setters - public function setName($name) - { - $this->name = $name; - } - - public function setLastName($lastName) - { - $this->lastName = $lastName; - } - -} \ No newline at end of file diff --git a/tests/Unit/Services/TestPerson/PersonNormalizer.php b/tests/Unit/Services/TestPerson/PersonNormalizer.php deleted file mode 100644 index c8b386dc..00000000 --- a/tests/Unit/Services/TestPerson/PersonNormalizer.php +++ /dev/null @@ -1,40 +0,0 @@ -normalizer = $normalizer; - } - - /** - * @param Person $object - * @throws \Symfony\Component\Serializer\Exception\ExceptionInterface - */ - public function normalize($object, string $format = null, array $context = []):array - { - $data = $this->normalizer->normalize($object,$format,$context); - return [ - 'name' => 'new_name', - 'lastname' => 'new_lastname', - ]; - } - - public function supportsNormalization($data, string $format = null, array $context = []):bool - { - return $data instanceof Person; - } - - -} \ No newline at end of file diff --git a/tests/Unit/Services/TestPerson/PersonTest.php b/tests/Unit/Services/TestPerson/PersonTest.php deleted file mode 100644 index 5d0b354f..00000000 --- a/tests/Unit/Services/TestPerson/PersonTest.php +++ /dev/null @@ -1,117 +0,0 @@ -setName('Kirill'); - $person->setLastName('Khramov'); - - - - $jsonContent = $serializer->serialize($person, 'json'); - - - self::assertNotEmpty($jsonContent); - echo $jsonContent; // or return it in a Response - } - - /** - * @test - */ - public function DeserializeTest():void{ - $encoders = [new XmlEncoder(), new JsonEncoder()]; - $normalizers = [new ObjectNormalizer()]; - - $serializer = new Serializer($normalizers, $encoders); - - $data = << - Kirill - Khramov - false - - EOF; - - $person = $serializer->deserialize($data, Person::class, 'xml'); - var_dump($person); - self::assertNotEmpty($person); - } - - /** - *@test - */ - public function NormalizePersonTest():void - { - $person = new Person(); - $encoders = [new XmlEncoder(), new JsonEncoder()]; - $normalizers = [new PersonNormalizer()]; - - $serializer = new Serializer($normalizers, $encoders); - - $person->setName('Kirill'); - $person->setLastName('Khramov'); - - $jsonContent = $serializer->serialize($person, 'json'); - self::assertNotEmpty($jsonContent); - echo $jsonContent; - - } - - /** - * @test - * @throws \Symfony\Component\Serializer\Exception\ExceptionInterface - */ - public function NormalizeMoneyTest():void - { - $encoders = [new XmlEncoder(), new JsonEncoder()]; - $normalizers = [new ObjectNormalizer()]; - - $serializer = new Serializer($normalizers, $encoders); - - $dollars = new Money(100, new Currency('USD')); - - $numberFormatter = new \NumberFormatter('en_US', \NumberFormatter::CURRENCY); - $intlFormatter = new IntlMoneyFormatter($numberFormatter, new ISOCurrencies()); - - $moneyFormatter = new AggregateMoneyFormatter([ - 'USD' => $intlFormatter, - ]); - - $money = $moneyFormatter->format($dollars); - $jsonContent = $serializer->normalize($money, null, [AbstractNormalizer::ATTRIBUTES => ['amount']]); - var_dump($jsonContent); - self::assertNotEmpty($money); - } - -} \ No newline at end of file From 5b837e4ee482493c278d8c4d4cea1c1ccf31fceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Sat, 10 Sep 2022 13:50:45 +0300 Subject: [PATCH 19/94] - edit method --- .../CRM/Deal/Service/DealProductRows.php | 18 +++++++----------- .../CRM/Deal/Service/DealProductRowsTest.php | 3 ++- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/Services/CRM/Deal/Service/DealProductRows.php b/src/Services/CRM/Deal/Service/DealProductRows.php index 9ef44840..e8955c47 100644 --- a/src/Services/CRM/Deal/Service/DealProductRows.php +++ b/src/Services/CRM/Deal/Service/DealProductRows.php @@ -75,19 +75,15 @@ public function getSuperSmart(int $dealId): DealProductRowItemsResult $res = $this->core->call('batch',[ 'halt'=>0, 'cmd'=>[ - $deal = new DealResult( $this->core->call('crm.deal.get', ['id' => $dealId])), - $rows = new DealProductRowItemsResult($this->core->call('crm.deal.productrows.get',['id' => $dealId,]),new Currency($deal->deal()->CURRENCY_ID)), - + 'deal' => sprintf('crm.deal.get?ID=%s', $dealId), + 'rows' => sprintf('crm.deal.productrows.get?ID=%s', $dealId) ], ]); - return $rows; - /*$data = $res->getResponseData()->getResult(); - $array = $data->getResultData()['result']['deal']['CURRENCY_ID']; - var_dump($array); - return new DealProductRowItemsResult( - $data->getResultData()['result']['deal']['ID'] , - new Currency($array) - );*/ + $data = $res->getResponseData()->getResult()->getResultData(); + $currency =$data['result']['deal']['CURRENCY_ID']; + $deal_test_id = $data['result']['deal']['ID']; + // А как нам вернуть DealProductRowItemsResult????(первый параметр принимает coreResponse) + return new DealProductRowItemsResult($deal_test_id,$currency); // todo Получить сделку и табличную часть за один запрос к Api } diff --git a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php index e5e92790..887fab9b 100644 --- a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php +++ b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php @@ -80,7 +80,7 @@ public function testGet(): void )->isSuccess() ); $currency = $callCosts->getCurrency(); - $res = $this->dealProductRowsService->getSuperSuperSmart($newDealId,$currency); + $res = $this->dealProductRowsService->getSuperSmart($newDealId); foreach ($res->getProductRows() as $productRow){ var_dump($productRow->PRICE); } @@ -117,6 +117,7 @@ public function testBatch():void{ print_r($data); } + public function setUp(): void { $this->dealService = Fabric::getServiceBuilder()->getCRMScope()->deal(); From ba6fbe8dbbe072388976e04ac704d2f198529a51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Sat, 10 Sep 2022 17:21:38 +0300 Subject: [PATCH 20/94] - delete serializer in composer.json and its dependencies - edit method in DealProductRows.php(work) --- composer.json | 2 - .../Deal/Result/DealProductRowItemResult.php | 6 --- .../CRM/Deal/Service/DealProductRows.php | 37 +++++++++---------- .../CRM/Deal/Service/DealProductRowsTest.php | 4 +- 4 files changed, 19 insertions(+), 30 deletions(-) diff --git a/composer.json b/composer.json index e09b099d..af46885c 100644 --- a/composer.json +++ b/composer.json @@ -29,8 +29,6 @@ "symfony/event-dispatcher": "5.4.* || 6.*", "ramsey/uuid": "^4.2.3", "moneyphp/money": "3.* || 4.*", - "symfony/serializer": "6.*||^6.1", - "symfony/property-access": "6.* || 6.1.*", "ext-intl": "*" }, "require-dev": { diff --git a/src/Services/CRM/Deal/Result/DealProductRowItemResult.php b/src/Services/CRM/Deal/Result/DealProductRowItemResult.php index 540d161f..da551122 100644 --- a/src/Services/CRM/Deal/Result/DealProductRowItemResult.php +++ b/src/Services/CRM/Deal/Result/DealProductRowItemResult.php @@ -46,10 +46,4 @@ public function __construct(array $data, Currency $currency) parent::__construct($data); $this->currency = $currency; } - - public function __get($offset) - { - return parent::__get($offset); - } - } \ No newline at end of file diff --git a/src/Services/CRM/Deal/Service/DealProductRows.php b/src/Services/CRM/Deal/Service/DealProductRows.php index e8955c47..d1a37a34 100644 --- a/src/Services/CRM/Deal/Service/DealProductRows.php +++ b/src/Services/CRM/Deal/Service/DealProductRows.php @@ -80,11 +80,8 @@ public function getSuperSmart(int $dealId): DealProductRowItemsResult ], ]); $data = $res->getResponseData()->getResult()->getResultData(); - $currency =$data['result']['deal']['CURRENCY_ID']; - $deal_test_id = $data['result']['deal']['ID']; - // А как нам вернуть DealProductRowItemsResult????(первый параметр принимает coreResponse) - return new DealProductRowItemsResult($deal_test_id,$currency); - // todo Получить сделку и табличную часть за один запрос к Api + $currency = new Currency($data['result']['deal']['CURRENCY_ID']); + return new DealProductRowItemsResult($res,$currency); } /** @@ -100,24 +97,24 @@ public function getSuperSuperSmart(int $dealId, Currency $currency = null): Deal $res = $this->core->call('batch',[ 'halt'=>0, 'cmd'=>[ - $deal = new DealResult( $this->core->call('crm.deal.get', ['id' => $dealId])), - $rows = new DealProductRowItemsResult($this->core->call('crm.deal.productrows.get',['id' => $dealId,]),new Currency($deal->deal()->CURRENCY_ID)), - + 'deal' => sprintf('crm.deal.get?ID=%s', $dealId), + 'rows' => sprintf('crm.deal.productrows.get?ID=%s', $dealId) ], ]); - return $rows; - }else{ - return new DealProductRowItemsResult( - $this->core->call( - 'crm.deal.productrows.get', - [ - 'id' => $dealId, - ] - ), - $currency - ); + $data = $res->getResponseData()->getResult()->getResultData(); + $currency = new Currency($data['result']['deal']['CURRENCY_ID']); + return new DealProductRowItemsResult($res,$currency); } - // todo Метод позволяет экономить один запрос если мы уже знаем валюту, а если не знаем то делает этот запрос. + + return new DealProductRowItemsResult( + $this->core->call( + 'crm.deal.productrows.get', + [ + 'id' => $dealId, + ] + ), + $currency + ); } /** diff --git a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php index 887fab9b..d3fb1458 100644 --- a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php +++ b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php @@ -63,7 +63,7 @@ public function testSet(): void */ public function testGet(): void { - $callCosts = new Money(1050, new Currency('USD')); + $callCosts = new Money(1050, new Currency('EUR')); $currencies = new ISOCurrencies(); $moneyFormatter = new DecimalMoneyFormatter($currencies); @@ -80,7 +80,7 @@ public function testGet(): void )->isSuccess() ); $currency = $callCosts->getCurrency(); - $res = $this->dealProductRowsService->getSuperSmart($newDealId); + $res = $this->dealProductRowsService->getSuperSuperSmart($newDealId); foreach ($res->getProductRows() as $productRow){ var_dump($productRow->PRICE); } From a05285e19d65e1ee50bb23d358e4ebb29a97eda0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Fri, 30 Sep 2022 16:50:12 +0300 Subject: [PATCH 21/94] - fix error phpstan --- .../CRM/Common/Result/AbstractCrmItem.php | 14 +++++++++++++ .../Deal/Result/DealProductRowItemResult.php | 10 --------- .../CRM/Deal/Service/DealProductRows.php | 21 ------------------- 3 files changed, 14 insertions(+), 31 deletions(-) diff --git a/src/Services/CRM/Common/Result/AbstractCrmItem.php b/src/Services/CRM/Common/Result/AbstractCrmItem.php index 980128b5..23112c8a 100644 --- a/src/Services/CRM/Common/Result/AbstractCrmItem.php +++ b/src/Services/CRM/Common/Result/AbstractCrmItem.php @@ -14,6 +14,20 @@ class AbstractCrmItem extends AbstractItem { private const CRM_USERFIELD_PREFIX = 'UF_CRM_'; + /** + * @var \Money\Currency + */ + private Currency $currency ; + + public function __construct(array $data, Currency $currency = null) + { + parent::__construct($data); + if ($currency !== null){ + $this->currency = $currency; + } + + } + /** * @param int|string $offset * diff --git a/src/Services/CRM/Deal/Result/DealProductRowItemResult.php b/src/Services/CRM/Deal/Result/DealProductRowItemResult.php index da551122..51af99eb 100644 --- a/src/Services/CRM/Deal/Result/DealProductRowItemResult.php +++ b/src/Services/CRM/Deal/Result/DealProductRowItemResult.php @@ -35,15 +35,5 @@ */ class DealProductRowItemResult extends AbstractCrmItem { - protected Currency $currency; - /** - * @param array $data - * @param \Money\Currency $currency - */ - public function __construct(array $data, Currency $currency) - { - parent::__construct($data); - $this->currency = $currency; - } } \ No newline at end of file diff --git a/src/Services/CRM/Deal/Service/DealProductRows.php b/src/Services/CRM/Deal/Service/DealProductRows.php index bd647c57..3ef4e644 100644 --- a/src/Services/CRM/Deal/Service/DealProductRows.php +++ b/src/Services/CRM/Deal/Service/DealProductRows.php @@ -43,27 +43,6 @@ public function getStupid(int $dealId, Currency $currency): DealProductRowItemsR ); } - /** - * @param int $dealId - * @return \Bitrix24\SDK\Services\CRM\Deal\Result\DealProductRowItemsResult - * @throws \Bitrix24\SDK\Core\Exceptions\BaseException - * @throws \Bitrix24\SDK\Core\Exceptions\TransportException - */ - public function getSmart(int $dealId): DealProductRowItemsResult - { - $deal = new DealResult($this->core->call('crm.deal.get', ['id' => $dealId])); - $currency = new Currency($deal->deal()->CURRENCY_ID); - return new DealProductRowItemsResult( - $this->core->call( - 'crm.deal.productrows.get', - [ - 'id' => $dealId, - ] - ), - $currency - ); - } - /** * @param int $dealId * @return \Bitrix24\SDK\Services\CRM\Deal\Result\DealProductRowItemsResult From c75b49eea485f9a68bdc43efffeeeaf0df25030b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Wed, 19 Oct 2022 22:32:37 +0300 Subject: [PATCH 22/94] =?UTF-8?q?-=20=D0=BF=D0=BE=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=83=20=D0=BC?= =?UTF-8?q?=D0=B5=D1=82=D0=BE=D0=B4=D0=B0=20get=20=D0=B2=20=D0=BE=D0=B1?= =?UTF-8?q?=D1=8A=D0=B5=D0=BA=D1=82=D0=B5=20DealProductRowItemsResult.php?= =?UTF-8?q?=20(=D0=A2=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20=D0=B2=20=D1=82?= =?UTF-8?q?=D0=B5=D1=81=D1=82=D0=B0=D1=85=20=D0=BC=D0=BE=D0=B6=D0=B5=D0=BC?= =?UTF-8?q?=20=D0=B8=20=D1=83=D0=BA=D0=B0=D0=B7=D1=8B=D0=B2=D0=B0=D1=82?= =?UTF-8?q?=D1=8C=20=D0=B2=D0=B0=D0=BB=D1=8E=D1=82=D1=83=20=D0=B8=D0=BB?= =?UTF-8?q?=D0=B8=20=D0=BD=D0=B5=20=D1=83=D0=BA=D0=B0=D0=B7=D1=8B=D0=B2?= =?UTF-8?q?=D0=B0=D1=82=D1=8C).=20-=20=D1=83=D0=B1=D1=80=D0=B0=D0=BB=20?= =?UTF-8?q?=D0=BB=D0=B8=D1=88=D0=BD=D0=B8=D0=B5=20=D0=BC=D0=B5=D1=82=D0=BE?= =?UTF-8?q?=D0=B4=D1=8B=20-=20=D0=BF=D0=BE=D0=BF=D1=80=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D1=82=D0=B5=D1=81=D1=82=D1=8B=20-=20=D0=B4=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20=D0=B2=20AbstractCrmItem.php?= =?UTF-8?q?=20=D0=BE=D1=81=D1=82=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=BB=D1=8F=20=D1=81=D0=B2=D1=8F=D0=B7=D0=B0=D0=BD?= =?UTF-8?q?=D0=BD=D1=8B=D0=B5=20=D1=81=20=D0=B4=D0=B5=D0=BD=D1=8C=D0=B3?= =?UTF-8?q?=D0=B0=D0=BC=D0=B8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CRM/Common/Result/AbstractCrmItem.php | 11 ++-- .../Deal/Result/DealProductRowItemsResult.php | 10 +++- .../CRM/Deal/Service/DealProductRows.php | 54 +++---------------- .../CRM/Deal/Service/DealProductRowsTest.php | 33 ++++++------ 4 files changed, 40 insertions(+), 68 deletions(-) diff --git a/src/Services/CRM/Common/Result/AbstractCrmItem.php b/src/Services/CRM/Common/Result/AbstractCrmItem.php index 23112c8a..f05d6cc8 100644 --- a/src/Services/CRM/Common/Result/AbstractCrmItem.php +++ b/src/Services/CRM/Common/Result/AbstractCrmItem.php @@ -17,12 +17,12 @@ class AbstractCrmItem extends AbstractItem /** * @var \Money\Currency */ - private Currency $currency ; + private Currency $currency; public function __construct(array $data, Currency $currency = null) { parent::__construct($data); - if ($currency !== null){ + if ($currency !== null) { $this->currency = $currency; } @@ -66,10 +66,13 @@ public function __get($offset) case 'HAS_IMOL': case 'OPENED': // deal + case 'PRICE_EXCLUSIVE': + case 'PRICE_NETTO': + case 'PRICE_BRUTTO': case 'PRICE': if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { - $var = $this->data[$offset] * 100; - return new Money((string)$var,new Currency($this->currency->getCode())); + $var = $this->data[$offset] * 100; + return new Money((string)$var, new Currency($this->currency->getCode())); } return null; case 'IS_MANUAL_OPPORTUNITY': diff --git a/src/Services/CRM/Deal/Result/DealProductRowItemsResult.php b/src/Services/CRM/Deal/Result/DealProductRowItemsResult.php index 25b15866..9cf683aa 100644 --- a/src/Services/CRM/Deal/Result/DealProductRowItemsResult.php +++ b/src/Services/CRM/Deal/Result/DealProductRowItemsResult.php @@ -32,8 +32,14 @@ public function __construct(Response $coreResponse,Currency $currency) public function getProductRows(): array { $res = []; - foreach ($this->getCoreResponse()->getResponseData()->getResult()['result']['rows'] as $productRow) { - $res[] = new DealProductRowItemResult($productRow,$this->currency); + if(!empty($this->getCoreResponse()->getResponseData()->getResult()['result']['rows'])) { + foreach ($this->getCoreResponse()->getResponseData()->getResult()['result']['rows'] as $productRow) { + $res[] = new DealProductRowItemResult($productRow, $this->currency); + } + } else { + foreach ($this->getCoreResponse()->getResponseData()->getResult() as $productRow) { + $res[] = new DealProductRowItemResult($productRow, $this->currency); + } } return $res; diff --git a/src/Services/CRM/Deal/Service/DealProductRows.php b/src/Services/CRM/Deal/Service/DealProductRows.php index 3ef4e644..42cacb1d 100644 --- a/src/Services/CRM/Deal/Service/DealProductRows.php +++ b/src/Services/CRM/Deal/Service/DealProductRows.php @@ -24,58 +24,18 @@ class DealProductRows extends AbstractService * * @link https://training.bitrix24.com/rest_help/crm/deals/crm_deal_productrows_get.php * - * @param int $dealId - * @param \Money\Currency $currency - * @return DealProductRowItemsResult - * @throws \Bitrix24\SDK\Core\Exceptions\BaseException - * @throws \Bitrix24\SDK\Core\Exceptions\TransportException - */ - public function getStupid(int $dealId, Currency $currency): DealProductRowItemsResult - { - return new DealProductRowItemsResult( - $this->core->call( - 'crm.deal.productrows.get', - [ - 'id' => $dealId, - ] - ), - $currency - ); - } - - /** - * @param int $dealId - * @return \Bitrix24\SDK\Services\CRM\Deal\Result\DealProductRowItemsResult - * @throws \Bitrix24\SDK\Core\Exceptions\BaseException - * @throws \Bitrix24\SDK\Core\Exceptions\TransportException - */ - public function getSuperSmart(int $dealId): DealProductRowItemsResult - { - $res = $this->core->call('batch',[ - 'halt'=>0, - 'cmd'=>[ - 'deal' => sprintf('crm.deal.get?ID=%s', $dealId), - 'rows' => sprintf('crm.deal.productrows.get?ID=%s', $dealId) - ], - ]); - $data = $res->getResponseData()->getResult(); - $currency = new Currency($data['result']['deal']['CURRENCY_ID']); - return new DealProductRowItemsResult($res,$currency); - } - - /** * @param int $dealId * @param \Money\Currency|null $currency * @return \Bitrix24\SDK\Services\CRM\Deal\Result\DealProductRowItemsResult * @throws \Bitrix24\SDK\Core\Exceptions\BaseException * @throws \Bitrix24\SDK\Core\Exceptions\TransportException */ - public function getSuperSuperSmart(int $dealId, Currency $currency = null): DealProductRowItemsResult + public function get(int $dealId, Currency $currency = null): DealProductRowItemsResult { - if ($currency === null){ - $res = $this->core->call('batch',[ - 'halt'=>0, - 'cmd'=>[ + if ($currency === null) { + $res = $this->core->call('batch', [ + 'halt' => 0, + 'cmd' => [ 'deal' => sprintf('crm.deal.get?ID=%s', $dealId), 'rows' => sprintf('crm.deal.productrows.get?ID=%s', $dealId) ], @@ -84,7 +44,6 @@ public function getSuperSuperSmart(int $dealId, Currency $currency = null): Deal $currency = new Currency($data['result']['deal']['CURRENCY_ID']); return new DealProductRowItemsResult($res,$currency); } - return new DealProductRowItemsResult( $this->core->call( 'crm.deal.productrows.get', @@ -92,10 +51,11 @@ public function getSuperSuperSmart(int $dealId, Currency $currency = null): Deal 'id' => $dealId, ] ), - $currency + $currency ); } + /** * Creates or updates product entries inside the specified deal. * diff --git a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php index 42a487e3..2c286da0 100644 --- a/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php +++ b/tests/Integration/Services/CRM/Deal/Service/DealProductRowsTest.php @@ -33,26 +33,24 @@ class DealProductRowsTest extends TestCase public function testSet(): void { - $callCosts = new Money(1050, new Currency('RUB')); + $callCosts = new Money(1050, new Currency('USD')); $currencies = new ISOCurrencies(); $moneyFormatter = new DecimalMoneyFormatter($currencies); $newDealId = $this->dealService->add(['TITLE' => 'test deal'])->getId(); - $this::assertCount(0, $this->dealProductRowsService->getSuperSmart($newDealId)->getProductRows()); + $this::assertCount(5, $this->dealProductRowsService->get($newDealId)->getProductRows()); $this::assertTrue( $this->dealProductRowsService->set( $newDealId, [ [ - 'PRODUCT_NAME' => 'qqqq', - 'PRICE'=> $moneyFormatter->format($callCosts), + 'PRODUCT_NAME' => 'wine', + 'PRICE' => $moneyFormatter->format($callCosts), ], ] )->isSuccess() ); - $this::assertCount(1, $this->dealProductRowsService->getSuperSmart($newDealId)->getProductRows()); - $mas = $this->dealProductRowsService->getSuperSmart($newDealId)->getProductRows()[0]; - $money = $moneyFormatter->format($mas->PRICE); + $this::assertCount(1, $this->dealProductRowsService->get($newDealId)->getProductRows()); } @@ -63,26 +61,31 @@ public function testSet(): void */ public function testGet(): void { - $callCosts = new Money(1050, new Currency('EUR')); + $callCosts = new Money(1050, new Currency('USD')); $currencies = new ISOCurrencies(); $moneyFormatter = new DecimalMoneyFormatter($currencies); - $newDealId = $this->dealService->add(['TITLE' => 'test deal','CURRENCY_ID'=>$callCosts->getCurrency()->getCode()])->getId(); + $newDealId = $this->dealService->add(['TITLE' => 'test deal', 'CURRENCY_ID' => $callCosts->getCurrency()->getCode()])->getId(); $this::assertTrue( $this->dealProductRowsService->set( $newDealId, [ [ - 'PRODUCT_NAME' => 'qqqq', - 'PRICE'=> $moneyFormatter->format($callCosts), + 'PRODUCT_NAME' => 'wine', + 'PRICE' => $moneyFormatter->format($callCosts), ], ] )->isSuccess() ); $currency = $callCosts->getCurrency(); - $res = $this->dealProductRowsService->getSuperSuperSmart($newDealId); - foreach ($res->getProductRows() as $productRow){ - var_dump($productRow->PRICE); + + $resultWithoutAvailableCurrency = $this->dealProductRowsService->get($newDealId); + $resultWithAvailableCurrency = $this->dealProductRowsService->get($newDealId, $currency); + foreach ($resultWithoutAvailableCurrency->getProductRows() as $productRow) { + $this::assertEquals($callCosts, $productRow->PRICE); + } + foreach ($resultWithAvailableCurrency->getProductRows() as $productRow) { + $this::assertEquals($callCosts, $productRow->PRICE); } } @@ -90,6 +93,6 @@ public function setUp(): void { $this->dealService = Fabric::getServiceBuilder()->getCRMScope()->deal(); $this->dealProductRowsService = Fabric::getServiceBuilder()->getCRMScope()->dealProductRows(); - $this->core=Fabric::getCore(); + $this->core = Fabric::getCore(); } } \ No newline at end of file From 9a33ff705a1da598f6c65712e28b8d2e16500706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Sun, 30 Oct 2022 18:51:33 +0300 Subject: [PATCH 23/94] - add bitrix24 interface and bitrix AccountStatus --- composer.json | 7 +- .../Bitrix24AccountInterface.php | 72 +++++++++++++++++++ .../Bitrix24Account/Bitrix24AccountStatus.php | 16 +++++ 3 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 src/Application/Contracts/Bitrix24Account/Bitrix24AccountInterface.php create mode 100644 src/Application/Contracts/Bitrix24Account/Bitrix24AccountStatus.php diff --git a/composer.json b/composer.json index 7a54f0ad..133000e4 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ } ], "require": { - "php": "7.4.*|8.*", + "php": "7.4.*|8.1*", "ext-json": "*", "ext-bcmath": "*", "ext-curl": "*", @@ -28,7 +28,8 @@ "symfony/http-foundation": "5.4.* || 6.*", "symfony/event-dispatcher": "5.4.* || 6.*", "ramsey/uuid": "^4.2.3", - "moneyphp/money": "3.* || 4.*" + "moneyphp/money": "3.* || 4.*", + "symfony/uid": "^6.0" }, "require-dev": { "monolog/monolog": "2.1.*", @@ -63,4 +64,4 @@ "vendor/bin/phpstan analyse --memory-limit 1G" ] } -} \ No newline at end of file +} diff --git a/src/Application/Contracts/Bitrix24Account/Bitrix24AccountInterface.php b/src/Application/Contracts/Bitrix24Account/Bitrix24AccountInterface.php new file mode 100644 index 00000000..dcde72d7 --- /dev/null +++ b/src/Application/Contracts/Bitrix24Account/Bitrix24AccountInterface.php @@ -0,0 +1,72 @@ + Date: Sun, 30 Oct 2022 19:20:07 +0300 Subject: [PATCH 24/94] - edit composer.json and update version php --- composer.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 133000e4..75a22d6b 100644 --- a/composer.json +++ b/composer.json @@ -14,10 +14,14 @@ { "name": "Maxim Mesilov", "homepage": "https://github.com/mesilov/" + }, + { + "name": "Kirill Hramov", + "homepage": "https://github.com/KarlsonComplete" } ], "require": { - "php": "7.4.*|8.1*", + "php": "8.1.*", "ext-json": "*", "ext-bcmath": "*", "ext-curl": "*", From c439a13edbbc1124a4c0e0b2aaec4494034a7c2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Sun, 30 Oct 2022 19:22:25 +0300 Subject: [PATCH 25/94] - update vendor-check.yml (version php -> 8.1) --- .github/workflows/vendor-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/vendor-check.yml b/.github/workflows/vendor-check.yml index 2ec82413..802c6d45 100644 --- a/.github/workflows/vendor-check.yml +++ b/.github/workflows/vendor-check.yml @@ -19,7 +19,7 @@ jobs: strategy: matrix: php-version: - - "7.4" + - "8.1" dependencies: [ highest ] steps: From a41e2d09d4a5971319acd557babaaf801588d508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Sun, 30 Oct 2022 19:46:33 +0300 Subject: [PATCH 26/94] - add contracts for bitrix24accountRepository --- composer.json | 5 +- .../Bitrix24AccountRepositoryInterface.php | 73 +++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 src/Application/Contracts/Bitrix24AccountRepositoryInterface.php diff --git a/composer.json b/composer.json index 7a54f0ad..cfa7a525 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,8 @@ "symfony/http-foundation": "5.4.* || 6.*", "symfony/event-dispatcher": "5.4.* || 6.*", "ramsey/uuid": "^4.2.3", - "moneyphp/money": "3.* || 4.*" + "moneyphp/money": "3.* || 4.*", + "symfony/uid": "^6.0" }, "require-dev": { "monolog/monolog": "2.1.*", @@ -63,4 +64,4 @@ "vendor/bin/phpstan analyse --memory-limit 1G" ] } -} \ No newline at end of file +} diff --git a/src/Application/Contracts/Bitrix24AccountRepositoryInterface.php b/src/Application/Contracts/Bitrix24AccountRepositoryInterface.php new file mode 100644 index 00000000..4513bc50 --- /dev/null +++ b/src/Application/Contracts/Bitrix24AccountRepositoryInterface.php @@ -0,0 +1,73 @@ + Date: Sun, 30 Oct 2022 20:25:52 +0300 Subject: [PATCH 27/94] - add SECURITY.md in root folder --- SECURITY.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..96736b8c --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,12 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 2.x | :white_check_mark: | +| 1.x | :x: | +| 0.x | :x: | + +## Reporting a Vulnerability +Create issue with vulnerability details \ No newline at end of file From 102c8459510dcc8d5ef4463f28edffa16bf6501c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Mon, 31 Oct 2022 14:07:12 +0300 Subject: [PATCH 28/94] - add new scope code - biconnector. --- src/Core/Credentials/Scope.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Core/Credentials/Scope.php b/src/Core/Credentials/Scope.php index 7a6117bb..123eb06c 100644 --- a/src/Core/Credentials/Scope.php +++ b/src/Core/Credentials/Scope.php @@ -18,6 +18,7 @@ class Scope */ protected array $availableScope = [ 'bizproc', + 'biconnector', 'calendar', 'call', 'cashbox', From d1c1b102157302f864b18dce89475072fe47dd50 Mon Sep 17 00:00:00 2001 From: mesilov Date: Mon, 31 Oct 2022 22:30:11 +0400 Subject: [PATCH 29/94] fix typehint Signed-off-by: mesilov --- CHANGELOG.md | 11 +++++++++++ src/Services/CRM/Contact/Result/ContactItemResult.php | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12e933af..e81c5a65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # bitrix24-php-sdk change log +## 2.0-beta.1 — 10.11.2022 + +### Added + +### Changed + +### Bugfix +* fix [typehint at ContactItemResult](https://github.com/mesilov/bitrix24-php-sdk/issues/320) + +### etc + ## 2.0-alpha.7 — 8.08.2022 ### Added diff --git a/src/Services/CRM/Contact/Result/ContactItemResult.php b/src/Services/CRM/Contact/Result/ContactItemResult.php index 82293707..e360e3eb 100644 --- a/src/Services/CRM/Contact/Result/ContactItemResult.php +++ b/src/Services/CRM/Contact/Result/ContactItemResult.php @@ -43,7 +43,7 @@ * @property-read DateTimeInterface $DATE_MODIFY * @property-read string $COMPANY_ID * @property-read string $COMPANY_IDS - * @property-read string $LEAD_ID + * @property-read int $LEAD_ID * @property-read string $ORIGINATOR_ID * @property-read string $ORIGIN_ID * @property-read string $ORIGIN_VERSION From 3074277611e7da47eeb9b66cb9b597f33d1cd6a6 Mon Sep 17 00:00:00 2001 From: mesilov Date: Mon, 31 Oct 2022 22:53:27 +0400 Subject: [PATCH 30/94] fix typehint in DealCategoryItemResult Signed-off-by: mesilov --- CHANGELOG.md | 1 + .../CRM/Common/Result/AbstractCrmItem.php | 4 ++++ .../CRM/Deal/Result/DealCategoryItemResult.php | 15 ++++++++------- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e81c5a65..08592589 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### Bugfix * fix [typehint at ContactItemResult](https://github.com/mesilov/bitrix24-php-sdk/issues/320) +* fix [return types in DealCategoryItemResult](https://github.com/mesilov/bitrix24-php-sdk/issues/322) ### etc diff --git a/src/Services/CRM/Common/Result/AbstractCrmItem.php b/src/Services/CRM/Common/Result/AbstractCrmItem.php index f05d6cc8..c05d6984 100644 --- a/src/Services/CRM/Common/Result/AbstractCrmItem.php +++ b/src/Services/CRM/Common/Result/AbstractCrmItem.php @@ -49,6 +49,8 @@ public function __get($offset) case 'QUOTE_ID': // productRow case 'OWNER_ID': + // DealCategoryItem + case 'SORT': if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { return (int)$this->data[$offset]; } @@ -78,11 +80,13 @@ public function __get($offset) case 'IS_MANUAL_OPPORTUNITY': case 'CLOSED': case 'IS_NEW': + case 'IS_LOCKED': case 'IS_RECURRING': case 'IS_RETURN_CUSTOMER': case 'IS_REPEATED_APPROACH': return $this->data[$offset] === 'Y'; case 'DATE_CREATE': + case 'CREATED_DATE': case 'DATE_MODIFY': case 'BIRTHDATE': case 'BEGINDATE': diff --git a/src/Services/CRM/Deal/Result/DealCategoryItemResult.php b/src/Services/CRM/Deal/Result/DealCategoryItemResult.php index 4dd57ab7..46d4c239 100644 --- a/src/Services/CRM/Deal/Result/DealCategoryItemResult.php +++ b/src/Services/CRM/Deal/Result/DealCategoryItemResult.php @@ -4,17 +4,18 @@ namespace Bitrix24\SDK\Services\CRM\Deal\Result; -use Bitrix24\SDK\Core\Result\AbstractItem; +use Bitrix24\SDK\Services\CRM\Common\Result\AbstractCrmItem; +use DateTimeImmutable; /** * Class DealItemResult * - * @property int $ID - * @property string $CREATED_DATE - * @property string $NAME - * @property string $IS_LOCKED - * @property string $SORT + * @property int $ID + * @property DateTimeImmutable $CREATED_DATE + * @property string $NAME + * @property bool $IS_LOCKED + * @property int $SORT */ -class DealCategoryItemResult extends AbstractItem +class DealCategoryItemResult extends AbstractCrmItem { } \ No newline at end of file From 01d29e1497cff238618ada95235a11f58b7a8ba3 Mon Sep 17 00:00:00 2001 From: mesilov Date: Mon, 31 Oct 2022 23:16:12 +0400 Subject: [PATCH 31/94] rename batch interface Signed-off-by: mesilov --- CHANGELOG.md | 1 + src/Core/Batch.php | 4 ++-- src/Core/BulkItemsReader/BulkItemsReaderBuilder.php | 12 ++++++------ .../FilterWithBatchWithoutCountOrder.php | 10 +++++----- ...tchInterface.php => BatchOperationsInterface.php} | 4 ++-- src/Services/AbstractBatchService.php | 10 +++++----- src/Services/AbstractServiceBuilder.php | 8 ++++---- src/Services/CRM/Deal/Service/Batch.php | 10 +++++----- src/Services/CRM/Lead/Service/Batch.php | 10 +++++----- tests/Unit/Stubs/NullBatch.php | 4 ++-- tools/PerformanceBenchmarks/ListCommand.php | 4 ++-- 11 files changed, 39 insertions(+), 38 deletions(-) rename src/Core/Contracts/{BatchInterface.php => BatchOperationsInterface.php} (97%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08592589..43c59228 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added ### Changed +* ❗️Batch interface `BatchInterface` [renamed](https://github.com/mesilov/bitrix24-php-sdk/issues/324) to `Bitrix24\SDK\Core\Contracts\BatchOperationsInterface` ### Bugfix * fix [typehint at ContactItemResult](https://github.com/mesilov/bitrix24-php-sdk/issues/320) diff --git a/src/Core/Batch.php b/src/Core/Batch.php index 10224162..d4123b87 100644 --- a/src/Core/Batch.php +++ b/src/Core/Batch.php @@ -6,7 +6,7 @@ use Bitrix24\SDK\Core\Commands\Command; use Bitrix24\SDK\Core\Commands\CommandCollection; -use Bitrix24\SDK\Core\Contracts\BatchInterface; +use Bitrix24\SDK\Core\Contracts\BatchOperationsInterface; use Bitrix24\SDK\Core\Contracts\CoreInterface; use Bitrix24\SDK\Core\Exceptions\BaseException; use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; @@ -23,7 +23,7 @@ * * @package Bitrix24\SDK\Core */ -class Batch implements BatchInterface +class Batch implements BatchOperationsInterface { private CoreInterface $core; private LoggerInterface $logger; diff --git a/src/Core/BulkItemsReader/BulkItemsReaderBuilder.php b/src/Core/BulkItemsReader/BulkItemsReaderBuilder.php index 32e99a0e..0c0fa8ea 100644 --- a/src/Core/BulkItemsReader/BulkItemsReaderBuilder.php +++ b/src/Core/BulkItemsReader/BulkItemsReaderBuilder.php @@ -5,7 +5,7 @@ namespace Bitrix24\SDK\Core\BulkItemsReader; use Bitrix24\SDK\Core\BulkItemsReader\ReadStrategies\FilterWithBatchWithoutCountOrder; -use Bitrix24\SDK\Core\Contracts\BatchInterface; +use Bitrix24\SDK\Core\Contracts\BatchOperationsInterface; use Bitrix24\SDK\Core\Contracts\BulkItemsReaderInterface; use Bitrix24\SDK\Core\Contracts\CoreInterface; use Psr\Log\LoggerInterface; @@ -13,16 +13,16 @@ class BulkItemsReaderBuilder { protected CoreInterface $core; - protected BatchInterface $batch; + protected BatchOperationsInterface $batch; protected LoggerInterface $logger; protected BulkItemsReaderInterface $readStrategy; /** - * @param \Bitrix24\SDK\Core\Contracts\CoreInterface $core - * @param \Bitrix24\SDK\Core\Contracts\BatchInterface $batch - * @param \Psr\Log\LoggerInterface $logger + * @param \Bitrix24\SDK\Core\Contracts\CoreInterface $core + * @param \Bitrix24\SDK\Core\Contracts\BatchOperationsInterface $batch + * @param \Psr\Log\LoggerInterface $logger */ - public function __construct(CoreInterface $core, BatchInterface $batch, LoggerInterface $logger) + public function __construct(CoreInterface $core, BatchOperationsInterface $batch, LoggerInterface $logger) { $this->core = $core; $this->batch = $batch; diff --git a/src/Core/BulkItemsReader/ReadStrategies/FilterWithBatchWithoutCountOrder.php b/src/Core/BulkItemsReader/ReadStrategies/FilterWithBatchWithoutCountOrder.php index ef58f410..b8eed12d 100644 --- a/src/Core/BulkItemsReader/ReadStrategies/FilterWithBatchWithoutCountOrder.php +++ b/src/Core/BulkItemsReader/ReadStrategies/FilterWithBatchWithoutCountOrder.php @@ -4,21 +4,21 @@ namespace Bitrix24\SDK\Core\BulkItemsReader\ReadStrategies; -use Bitrix24\SDK\Core\Contracts\BatchInterface; +use Bitrix24\SDK\Core\Contracts\BatchOperationsInterface; use Bitrix24\SDK\Core\Contracts\BulkItemsReaderInterface; use Generator; use Psr\Log\LoggerInterface; class FilterWithBatchWithoutCountOrder implements BulkItemsReaderInterface { - private BatchInterface $batch; + private BatchOperationsInterface $batch; private LoggerInterface $log; /** - * @param \Bitrix24\SDK\Core\Contracts\BatchInterface $batch - * @param \Psr\Log\LoggerInterface $log + * @param \Bitrix24\SDK\Core\Contracts\BatchOperationsInterface $batch + * @param \Psr\Log\LoggerInterface $log */ - public function __construct(BatchInterface $batch, LoggerInterface $log) + public function __construct(BatchOperationsInterface $batch, LoggerInterface $log) { $this->batch = $batch; $this->log = $log; diff --git a/src/Core/Contracts/BatchInterface.php b/src/Core/Contracts/BatchOperationsInterface.php similarity index 97% rename from src/Core/Contracts/BatchInterface.php rename to src/Core/Contracts/BatchOperationsInterface.php index b6f0744e..801beab6 100644 --- a/src/Core/Contracts/BatchInterface.php +++ b/src/Core/Contracts/BatchOperationsInterface.php @@ -9,11 +9,11 @@ use Generator; /** - * Interface BatchInterface + * Interface BatchOperationsInterface * * @package Bitrix24\SDK\Core\Contracts */ -interface BatchInterface +interface BatchOperationsInterface { /** * Batch wrapper for *.list methods without counting elements on every api-call diff --git a/src/Services/AbstractBatchService.php b/src/Services/AbstractBatchService.php index c3d30071..c62a6b69 100644 --- a/src/Services/AbstractBatchService.php +++ b/src/Services/AbstractBatchService.php @@ -4,7 +4,7 @@ namespace Bitrix24\SDK\Services; -use Bitrix24\SDK\Core\Contracts\BatchInterface; +use Bitrix24\SDK\Core\Contracts\BatchOperationsInterface; use Psr\Log\LoggerInterface; /** @@ -14,16 +14,16 @@ */ abstract class AbstractBatchService { - protected BatchInterface $batch; + protected BatchOperationsInterface $batch; protected LoggerInterface $log; /** * Batch constructor. * - * @param BatchInterface $batch - * @param LoggerInterface $log + * @param BatchOperationsInterface $batch + * @param LoggerInterface $log */ - public function __construct(BatchInterface $batch, LoggerInterface $log) + public function __construct(BatchOperationsInterface $batch, LoggerInterface $log) { $this->batch = $batch; $this->log = $log; diff --git a/src/Services/AbstractServiceBuilder.php b/src/Services/AbstractServiceBuilder.php index 186b5867..36c97565 100644 --- a/src/Services/AbstractServiceBuilder.php +++ b/src/Services/AbstractServiceBuilder.php @@ -5,7 +5,7 @@ namespace Bitrix24\SDK\Services; -use Bitrix24\SDK\Core\Contracts\BatchInterface; +use Bitrix24\SDK\Core\Contracts\BatchOperationsInterface; use Bitrix24\SDK\Core\Contracts\BulkItemsReaderInterface; use Bitrix24\SDK\Core\Contracts\CoreInterface; use Psr\Log\LoggerInterface; @@ -18,7 +18,7 @@ abstract class AbstractServiceBuilder { protected CoreInterface $core; - protected BatchInterface $batch; + protected BatchOperationsInterface $batch; protected BulkItemsReaderInterface $bulkItemsReader; protected LoggerInterface $log; protected array $serviceCache; @@ -27,13 +27,13 @@ abstract class AbstractServiceBuilder * AbstractServiceBuilder constructor. * * @param CoreInterface $core - * @param BatchInterface $batch + * @param BatchOperationsInterface $batch * @param \Bitrix24\SDK\Core\Contracts\BulkItemsReaderInterface $bulkItemsReader * @param LoggerInterface $log */ public function __construct( CoreInterface $core, - BatchInterface $batch, + BatchOperationsInterface $batch, BulkItemsReaderInterface $bulkItemsReader, LoggerInterface $log ) { diff --git a/src/Services/CRM/Deal/Service/Batch.php b/src/Services/CRM/Deal/Service/Batch.php index e5351495..6be17a0a 100644 --- a/src/Services/CRM/Deal/Service/Batch.php +++ b/src/Services/CRM/Deal/Service/Batch.php @@ -4,7 +4,7 @@ namespace Bitrix24\SDK\Services\CRM\Deal\Service; -use Bitrix24\SDK\Core\Contracts\BatchInterface; +use Bitrix24\SDK\Core\Contracts\BatchOperationsInterface; use Bitrix24\SDK\Core\Exceptions\BaseException; use Bitrix24\SDK\Core\Result\AddedItemBatchResult; use Bitrix24\SDK\Core\Result\DeletedItemBatchResult; @@ -20,16 +20,16 @@ */ class Batch { - protected BatchInterface $batch; + protected BatchOperationsInterface $batch; protected LoggerInterface $log; /** * Batch constructor. * - * @param BatchInterface $batch - * @param LoggerInterface $log + * @param BatchOperationsInterface $batch + * @param LoggerInterface $log */ - public function __construct(BatchInterface $batch, LoggerInterface $log) + public function __construct(BatchOperationsInterface $batch, LoggerInterface $log) { $this->batch = $batch; $this->log = $log; diff --git a/src/Services/CRM/Lead/Service/Batch.php b/src/Services/CRM/Lead/Service/Batch.php index 934457c5..e34bdbbb 100644 --- a/src/Services/CRM/Lead/Service/Batch.php +++ b/src/Services/CRM/Lead/Service/Batch.php @@ -4,7 +4,7 @@ namespace Bitrix24\SDK\Services\CRM\Lead\Service; -use Bitrix24\SDK\Core\Contracts\BatchInterface; +use Bitrix24\SDK\Core\Contracts\BatchOperationsInterface; use Bitrix24\SDK\Core\Exceptions\BaseException; use Bitrix24\SDK\Core\Result\AddedItemBatchResult; use Bitrix24\SDK\Core\Result\DeletedItemBatchResult; @@ -19,16 +19,16 @@ */ class Batch { - protected BatchInterface $batch; + protected BatchOperationsInterface $batch; protected LoggerInterface $log; /** * Batch constructor. * - * @param BatchInterface $batch - * @param LoggerInterface $log + * @param BatchOperationsInterface $batch + * @param LoggerInterface $log */ - public function __construct(BatchInterface $batch, LoggerInterface $log) + public function __construct(BatchOperationsInterface $batch, LoggerInterface $log) { $this->batch = $batch; $this->log = $log; diff --git a/tests/Unit/Stubs/NullBatch.php b/tests/Unit/Stubs/NullBatch.php index 14ddbb6f..f775dab6 100644 --- a/tests/Unit/Stubs/NullBatch.php +++ b/tests/Unit/Stubs/NullBatch.php @@ -4,7 +4,7 @@ namespace Bitrix24\SDK\Tests\Unit\Stubs; -use Bitrix24\SDK\Core\Contracts\BatchInterface; +use Bitrix24\SDK\Core\Contracts\BatchOperationsInterface; use Bitrix24\SDK\Core\Exceptions\BaseException; use Bitrix24\SDK\Core\Response\DTO\ResponseData; use Generator; @@ -14,7 +14,7 @@ * * @package Bitrix24\SDK\Tests\Unit\Stubs */ -class NullBatch implements BatchInterface +class NullBatch implements BatchOperationsInterface { /** diff --git a/tools/PerformanceBenchmarks/ListCommand.php b/tools/PerformanceBenchmarks/ListCommand.php index 1a5d06f7..a06c78a9 100644 --- a/tools/PerformanceBenchmarks/ListCommand.php +++ b/tools/PerformanceBenchmarks/ListCommand.php @@ -5,7 +5,7 @@ namespace Bitrix24\SDK\Tools\PerformanceBenchmarks; use Bitrix24\SDK\Core\Batch; -use Bitrix24\SDK\Core\Contracts\BatchInterface; +use Bitrix24\SDK\Core\Contracts\BatchOperationsInterface; use Bitrix24\SDK\Core\Contracts\CoreInterface; use Bitrix24\SDK\Core\CoreBuilder; use Bitrix24\SDK\Core\Credentials\Credentials; @@ -34,7 +34,7 @@ class ListCommand extends Command { protected LoggerInterface $logger; protected CoreInterface $core; - protected BatchInterface $batch; + protected BatchOperationsInterface $batch; /** * @var string */ From 831c13aeac04e95dcf9a43a0260c57131d958870 Mon Sep 17 00:00:00 2001 From: mesilov Date: Mon, 31 Oct 2022 23:33:11 +0400 Subject: [PATCH 32/94] fix contracts for apps Signed-off-by: mesilov --- CHANGELOG.md | 2 ++ .../Bitrix24AccountRepositoryInterface.php | 20 +++++++++---------- 2 files changed, 12 insertions(+), 10 deletions(-) rename src/Application/Contracts/{ => Bitrix24Account}/Bitrix24AccountRepositoryInterface.php (72%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43c59228..91ad69e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## 2.0-beta.1 — 10.11.2022 ### Added +* add `Symfony\Component\Uid\Uuid` requirements +* add contracts for bitrix24 applications based on bitrix24-php-sdk - `Bitrix24\SDK\Application\Contracts`, now added `Bitrix24Account` ### Changed * ❗️Batch interface `BatchInterface` [renamed](https://github.com/mesilov/bitrix24-php-sdk/issues/324) to `Bitrix24\SDK\Core\Contracts\BatchOperationsInterface` diff --git a/src/Application/Contracts/Bitrix24AccountRepositoryInterface.php b/src/Application/Contracts/Bitrix24Account/Bitrix24AccountRepositoryInterface.php similarity index 72% rename from src/Application/Contracts/Bitrix24AccountRepositoryInterface.php rename to src/Application/Contracts/Bitrix24Account/Bitrix24AccountRepositoryInterface.php index 4513bc50..deeae92e 100644 --- a/src/Application/Contracts/Bitrix24AccountRepositoryInterface.php +++ b/src/Application/Contracts/Bitrix24Account/Bitrix24AccountRepositoryInterface.php @@ -2,24 +2,24 @@ declare(strict_types=1); -namespace Bitrix24\SDK\Application\Contracts; +namespace Bitrix24\SDK\Application\Contracts\Bitrix24Account; use Symfony\Component\Uid\Uuid; interface Bitrix24AccountRepositoryInterface { /** - * Сохранение аккаунта + * Save account * * @param Bitrix24AccountInterface $entity - * @param bool $flush + * @param bool $flush * * @return void */ public function saveAccount(Bitrix24AccountInterface $entity, bool $flush = false): void; /** - * Получение аккаунта по его идентификатору + * Get by account id * * @param \Symfony\Component\Uid\Uuid $id * @@ -28,24 +28,24 @@ public function saveAccount(Bitrix24AccountInterface $entity, bool $flush = fals public function getById(Uuid $id): Bitrix24AccountInterface; /** - * Физическое удаление аккаунта + * Delete account * * @param Bitrix24AccountInterface $entity - * @param bool $flush + * @param bool $flush * * @return void */ public function deleteAccount(Bitrix24AccountInterface $entity, bool $flush = false): void; /** - * Поиск аккаунта по member_id + * Find account by member_id * * @return ?Bitrix24AccountInterface Returns an array of Bitrix24Account objects */ public function findAccountByMemberId(string $memberId): ?Bitrix24AccountInterface; /** - * Получение аккаунта по member_id + * Get account by member_id * * @param string $memberId * @@ -54,7 +54,7 @@ public function findAccountByMemberId(string $memberId): ?Bitrix24AccountInterfa public function getAccountByMemberId(string $memberId): Bitrix24AccountInterface; /** - * Поиск аккаунта по идентификатору контактного лица + * Find account by contact person id - person, who installed application * * @param \Symfony\Component\Uid\Uuid $contactPersonId * @@ -63,7 +63,7 @@ public function getAccountByMemberId(string $memberId): Bitrix24AccountInterface public function findAccountByContactPersonId(Uuid $contactPersonId): ?Bitrix24AccountInterface; /** - * Поиск аккаунта по URL домена + * Find account by domain url * * @param string $domainUrl * From e1a6ea541f79af93b1c594abf138454eb3f10c3b Mon Sep 17 00:00:00 2001 From: mesilov Date: Tue, 1 Nov 2022 02:15:09 +0400 Subject: [PATCH 33/94] add service builder factory Signed-off-by: mesilov --- CHANGELOG.md | 1 + src/Services/ServiceBuilderFactory.php | 123 +++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 src/Services/ServiceBuilderFactory.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 91ad69e3..6286db57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added * add `Symfony\Component\Uid\Uuid` requirements * add contracts for bitrix24 applications based on bitrix24-php-sdk - `Bitrix24\SDK\Application\Contracts`, now added `Bitrix24Account` +* add [service builder factory](https://github.com/mesilov/bitrix24-php-sdk/issues/328) ### Changed * ❗️Batch interface `BatchInterface` [renamed](https://github.com/mesilov/bitrix24-php-sdk/issues/324) to `Bitrix24\SDK\Core\Contracts\BatchOperationsInterface` diff --git a/src/Services/ServiceBuilderFactory.php b/src/Services/ServiceBuilderFactory.php new file mode 100644 index 00000000..5bef3253 --- /dev/null +++ b/src/Services/ServiceBuilderFactory.php @@ -0,0 +1,123 @@ +eventDispatcher = $eventDispatcher; + $this->log = $log; + } + + /** + * Init service builder from application account + * + * @param ApplicationProfile $applicationProfile + * @param Bitrix24AccountInterface $bitrix24Account + * + * @return \Bitrix24\SDK\Services\ServiceBuilder + * @throws \Bitrix24\SDK\Core\Exceptions\InvalidArgumentException + */ + public function initFromAccount(ApplicationProfile $applicationProfile, Bitrix24AccountInterface $bitrix24Account): ServiceBuilder + { + return $this->getServiceBuilder( + Credentials::createFromOAuth( + AccessToken::initFromArray( + [ + 'access_token' => $bitrix24Account->getAccessToken(), + 'refresh_token' => $bitrix24Account->getRefreshToken(), + 'expires' => $bitrix24Account->getExpires(), + ] + ), + $applicationProfile, + $bitrix24Account->getDomainUrl() + ) + ); + } + + /** + * Init service builder from request + * + * @param \Bitrix24\SDK\Core\Credentials\ApplicationProfile $applicationProfile + * @param \Bitrix24\SDK\Core\Credentials\AccessToken $accessToken + * @param string $bitrix24DomainUrl + * + * @return \Bitrix24\SDK\Services\ServiceBuilder + * @throws \Bitrix24\SDK\Core\Exceptions\InvalidArgumentException + */ + public function initFromRequest( + ApplicationProfile $applicationProfile, + AccessToken $accessToken, + string $bitrix24DomainUrl + ): ServiceBuilder { + return $this->getServiceBuilder( + Credentials::createFromOAuth( + $accessToken, + $applicationProfile, + $bitrix24DomainUrl + ) + ); + } + + /** + * Init service builder from webhook + * + * @param string $webhookUrl + * + * @return \Bitrix24\SDK\Services\ServiceBuilder + * @throws \Bitrix24\SDK\Core\Exceptions\InvalidArgumentException + */ + public function initFromWebhook(string $webhookUrl): ServiceBuilder + { + return $this->getServiceBuilder(Credentials::createFromWebhook(new WebhookUrl($webhookUrl))); + } + + /** + * @param \Bitrix24\SDK\Core\Credentials\Credentials $credentials + * + * @return \Bitrix24\SDK\Services\ServiceBuilder + * @throws \Bitrix24\SDK\Core\Exceptions\InvalidArgumentException + */ + private function getServiceBuilder(Credentials $credentials): ServiceBuilder + { + $core = (new CoreBuilder()) + ->withEventDispatcher($this->eventDispatcher) + ->withLogger($this->log) + ->withCredentials($credentials) + ->build(); + $batch = new Batch($core, $this->log); + + return new ServiceBuilder( + $core, + $batch, + (new BulkItemsReaderBuilder( + $core, + $batch, + $this->log + ))->build(), + $this->log + ); + } + +} \ No newline at end of file From 0544bc6efad99fef48281a6fd4d33e98b95a6321 Mon Sep 17 00:00:00 2001 From: mesilov Date: Wed, 23 Nov 2022 12:19:07 +0400 Subject: [PATCH 34/94] add OnVoximplantCallInit and OnVoximplantCallStart Signed-off-by: mesilov --- CHANGELOG.md | 20 +++++-- src/Application/ApplicationStatus.php | 11 ++++ src/Core/Credentials/Scope.php | 8 +++ .../Telephony/Requests/Events/Auth.php | 44 +++++++++++++++ .../Requests/Events/OnVoximplantCallInit.php | 54 ------------------- .../Events/OnVoximplantCallInit/CallData.php | 32 +++++++++++ .../OnVoximplantCallInit.php | 32 +++++++++++ .../Requests/Events/OnVoximplantCallStart.php | 29 ---------- .../Events/OnVoximplantCallStart/CallData.php | 28 ++++++++++ .../OnVoximplantCallStart.php | 29 ++++++++++ .../Application/ApplicationStatusTest.php | 12 ++++- tests/Unit/Core/Credentials/ScopeTest.php | 12 +++++ 12 files changed, 223 insertions(+), 88 deletions(-) create mode 100644 src/Services/Telephony/Requests/Events/Auth.php delete mode 100644 src/Services/Telephony/Requests/Events/OnVoximplantCallInit.php create mode 100644 src/Services/Telephony/Requests/Events/OnVoximplantCallInit/CallData.php create mode 100644 src/Services/Telephony/Requests/Events/OnVoximplantCallInit/OnVoximplantCallInit.php delete mode 100644 src/Services/Telephony/Requests/Events/OnVoximplantCallStart.php create mode 100644 src/Services/Telephony/Requests/Events/OnVoximplantCallStart/CallData.php create mode 100644 src/Services/Telephony/Requests/Events/OnVoximplantCallStart/OnVoximplantCallStart.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 6286db57..9f4e99fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,16 +3,28 @@ ## 2.0-beta.1 — 10.11.2022 ### Added + * add `Symfony\Component\Uid\Uuid` requirements -* add contracts for bitrix24 applications based on bitrix24-php-sdk - `Bitrix24\SDK\Application\Contracts`, now added `Bitrix24Account` +* add contracts for bitrix24 applications based on bitrix24-php-sdk - `Bitrix24\SDK\Application\Contracts`, now added `Bitrix24Account` * add [service builder factory](https://github.com/mesilov/bitrix24-php-sdk/issues/328) +* add method `Bitrix24\SDK\Core\Credentials\Scope::initFromString` +* add method `Bitrix24\SDK\Application\ApplicationStatus::initFromString` ### Changed -* ❗️Batch interface `BatchInterface` [renamed](https://github.com/mesilov/bitrix24-php-sdk/issues/324) to `Bitrix24\SDK\Core\Contracts\BatchOperationsInterface` + +* ❗️Batch interface `BatchInterface` [renamed](https://github.com/mesilov/bitrix24-php-sdk/issues/324) + to `Bitrix24\SDK\Core\Contracts\BatchOperationsInterface` +* ❗`Bitrix24\SDK\Services\Telephony\Requests\Events` moved to separated namespaces: + * from `Bitrix24\SDK\Services\Telephony\Requests\Events\OnVoximplantCallInit` + to `Bitrix24\SDK\Services\Telephony\Requests\Events\OnVoximplantCallInit\OnVoximplantCallInit` + * from `Bitrix24\SDK\Services\Telephony\Requests\Events\OnVoximplantCallStart` + to `Bitrix24\SDK\Services\Telephony\Requests\Events\OnVoximplantCallStart\OnVoximplantCallStart` ### Bugfix + * fix [typehint at ContactItemResult](https://github.com/mesilov/bitrix24-php-sdk/issues/320) * fix [return types in DealCategoryItemResult](https://github.com/mesilov/bitrix24-php-sdk/issues/322) +* fix [add auth node in telephony voximplant events requests](https://github.com/mesilov/bitrix24-php-sdk/issues/331) ### etc @@ -62,10 +74,10 @@ * method `Services\Main\Service::getAllMethods` marks as deprecated * method `Services\Main\Service::getMethodsByScope` marks as deprecated * ❗️fabric methods `Bitrix24\SDK\Core\Credentials` - renamed and now are [consistent](https://github.com/mesilov/bitrix24-php-sdk/issues/303): `createFromWebhook`, `createFromOAuth` + renamed and now are [consistent](https://github.com/mesilov/bitrix24-php-sdk/issues/303): `createFromWebhook`, `createFromOAuth` , `createFromPlacementRequest` * ❗️deleted [unused class](https://github.com/mesilov/bitrix24-php-sdk/issues/303) `Bitrix24\SDK\Core\Response\DTO\ResponseDataCollection` -* ❗️deleted [redundant class](https://github.com/mesilov/bitrix24-php-sdk/issues/303) `Bitrix24\SDK\Core\Response\DTO\Result` +* ❗️deleted [redundant class](https://github.com/mesilov/bitrix24-php-sdk/issues/303) `Bitrix24\SDK\Core\Response\DTO\Result` * ❗️deleted [method](https://github.com/mesilov/bitrix24-php-sdk/issues/303) `CoreBuilder::withWebhookUrl`, use method `CoreBuilder::withCredentials` diff --git a/src/Application/ApplicationStatus.php b/src/Application/ApplicationStatus.php index 54500ccf..3fefdbc8 100644 --- a/src/Application/ApplicationStatus.php +++ b/src/Application/ApplicationStatus.php @@ -116,4 +116,15 @@ public static function initFromRequest(Request $request): self { return new self($request->request->getAlpha('status')); } + + /** + * @param string $shortStatusCode + * + * @return static + * @throws \Bitrix24\SDK\Core\Exceptions\InvalidArgumentException + */ + public static function initFromString(string $shortStatusCode): self + { + return new self($shortStatusCode); + } } \ No newline at end of file diff --git a/src/Core/Credentials/Scope.php b/src/Core/Credentials/Scope.php index 123eb06c..6e64d1aa 100644 --- a/src/Core/Credentials/Scope.php +++ b/src/Core/Credentials/Scope.php @@ -100,4 +100,12 @@ public function getScopeCodes(): array { return $this->currentScope; } + + /** + * @throws \Bitrix24\SDK\Core\Exceptions\UnknownScopeCodeException + */ + public static function initFromString(string $scope): self + { + return new self(str_replace(' ', '', explode(',', $scope))); + } } \ No newline at end of file diff --git a/src/Services/Telephony/Requests/Events/Auth.php b/src/Services/Telephony/Requests/Events/Auth.php new file mode 100644 index 00000000..b7c2e04a --- /dev/null +++ b/src/Services/Telephony/Requests/Events/Auth.php @@ -0,0 +1,44 @@ + Scope::initFromString((string)$this->data[$offset]), + 'status' => ApplicationStatus::initFromString((string)$this->data[$offset]), + 'user_id', 'expires_in', 'expires' => (int)$this->data[$offset], + default => parent::__get($offset), + }; + } +} + + + diff --git a/src/Services/Telephony/Requests/Events/OnVoximplantCallInit.php b/src/Services/Telephony/Requests/Events/OnVoximplantCallInit.php deleted file mode 100644 index 753f2d67..00000000 --- a/src/Services/Telephony/Requests/Events/OnVoximplantCallInit.php +++ /dev/null @@ -1,54 +0,0 @@ -eventPayload['data']['CALL_ID']; - } - - /** - * @throws \Bitrix24\SDK\Core\Exceptions\InvalidArgumentException - */ - public function getCallType(): CallType - { - return CallType::initByTypeCode((int)$this->eventPayload['data']['CALL_TYPE']); - } - - /** - * @return string Line ID (numeric for leased PBX, regXXX for cloud hosted PBX, and sipXXX for office PBX). - */ - public function getAccountSearchId(): string - { - return $this->eventPayload['data']['ACCOUNT_SEARCH_ID']; - } - - /** - * @return string Number called by the operator (if call type is: 1 – Outbound) or number called by the subscriber (if call type is: 2 – Inbound). - */ - public function getPhoneNumber(): string - { - return $this->eventPayload['data']['PHONE_NUMBER']; - } - - /** - * @return string Line identifier (if call type is: 1 – Outbound) or telephone number used to make a call to the portal (if call type is: 2 – Inbound). - */ - public function getCallerId(): string - { - return $this->eventPayload['data']['CALLER_ID']; - } -} \ No newline at end of file diff --git a/src/Services/Telephony/Requests/Events/OnVoximplantCallInit/CallData.php b/src/Services/Telephony/Requests/Events/OnVoximplantCallInit/CallData.php new file mode 100644 index 00000000..565ed6d7 --- /dev/null +++ b/src/Services/Telephony/Requests/Events/OnVoximplantCallInit/CallData.php @@ -0,0 +1,32 @@ + CallType::initByTypeCode((int)$this->data[$offset]), + 'REST_APP_ID' => (int)$this->data[$offset], + default => parent::__get($offset), + }; + } +} \ No newline at end of file diff --git a/src/Services/Telephony/Requests/Events/OnVoximplantCallInit/OnVoximplantCallInit.php b/src/Services/Telephony/Requests/Events/OnVoximplantCallInit/OnVoximplantCallInit.php new file mode 100644 index 00000000..2e0c2889 --- /dev/null +++ b/src/Services/Telephony/Requests/Events/OnVoximplantCallInit/OnVoximplantCallInit.php @@ -0,0 +1,32 @@ +eventPayload['auth']); + } + + /** + * @return \Bitrix24\SDK\Services\Telephony\Requests\Events\OnVoximplantCallInit\CallData + */ + public function getCallData(): CallData + { + return new CallData($this->eventPayload['data']); + } +} \ No newline at end of file diff --git a/src/Services/Telephony/Requests/Events/OnVoximplantCallStart.php b/src/Services/Telephony/Requests/Events/OnVoximplantCallStart.php deleted file mode 100644 index 11df4201..00000000 --- a/src/Services/Telephony/Requests/Events/OnVoximplantCallStart.php +++ /dev/null @@ -1,29 +0,0 @@ -eventPayload['data']['CALL_ID']; - } - - /** - * @return int Identifier of the user who responded the call. - */ - public function getUserId(): int - { - return (int)$this->eventPayload['data']['USER_ID']; - } -} \ No newline at end of file diff --git a/src/Services/Telephony/Requests/Events/OnVoximplantCallStart/CallData.php b/src/Services/Telephony/Requests/Events/OnVoximplantCallStart/CallData.php new file mode 100644 index 00000000..86a08f01 --- /dev/null +++ b/src/Services/Telephony/Requests/Events/OnVoximplantCallStart/CallData.php @@ -0,0 +1,28 @@ + (int)$this->data[$offset], + default => parent::__get($offset), + }; + } +} \ No newline at end of file diff --git a/src/Services/Telephony/Requests/Events/OnVoximplantCallStart/OnVoximplantCallStart.php b/src/Services/Telephony/Requests/Events/OnVoximplantCallStart/OnVoximplantCallStart.php new file mode 100644 index 00000000..6138ac4f --- /dev/null +++ b/src/Services/Telephony/Requests/Events/OnVoximplantCallStart/OnVoximplantCallStart.php @@ -0,0 +1,29 @@ +eventPayload['auth']); + } + /** + * @return \Bitrix24\SDK\Services\Telephony\Requests\Events\OnVoximplantCallStart\CallData + */ + public function getCallData(): CallData + { + return new CallData($this->eventPayload['data']); + } +} \ No newline at end of file diff --git a/tests/Unit/Application/ApplicationStatusTest.php b/tests/Unit/Application/ApplicationStatusTest.php index cdf70fb6..d768b0e4 100644 --- a/tests/Unit/Application/ApplicationStatusTest.php +++ b/tests/Unit/Application/ApplicationStatusTest.php @@ -19,7 +19,7 @@ class ApplicationStatusTest extends TestCase * @dataProvider statusDataProvider * @throws \Bitrix24\SDK\Core\Exceptions\InvalidArgumentException */ - public function testGetStatusCode(string $shortCode, string $longCode) + public function testGetStatusCode(string $shortCode, string $longCode): void { $this->assertEquals( $longCode, @@ -36,6 +36,16 @@ public function testInvalidStatusCode(): void new ApplicationStatus('foo'); } + /** + * @return void + * @throws \Bitrix24\SDK\Core\Exceptions\InvalidArgumentException + * @covers \Bitrix24\SDK\Application\ApplicationStatus::initFromString + */ + public function testInitFromString(): void + { + $this->assertTrue(ApplicationStatus::initFromString('F')->isFree()); + } + /** * @return \Generator */ diff --git a/tests/Unit/Core/Credentials/ScopeTest.php b/tests/Unit/Core/Credentials/ScopeTest.php index 5ffd952d..d7d37b32 100644 --- a/tests/Unit/Core/Credentials/ScopeTest.php +++ b/tests/Unit/Core/Credentials/ScopeTest.php @@ -82,4 +82,16 @@ public function testWrongScopeCode(): void $this->assertEquals(['crm', 'call', 'im'], $scope->getScopeCodes()); } + + /** + * @return void + * @throws \Bitrix24\SDK\Core\Exceptions\UnknownScopeCodeException + * @covers \Bitrix24\SDK\Core\Credentials\Scope::initFromString + * @testdox Test init Scope from string + */ + public function testInitFromString(): void + { + $scope = Scope::initFromString('crm,telephony,call,user_basic,placement,im,imopenlines'); + $this->assertEquals(['crm', 'telephony', 'call', 'user_basic', 'placement', 'im', 'imopenlines'], $scope->getScopeCodes()); + } } From c834b376902d856e7cae3940e00e1835cd2666b8 Mon Sep 17 00:00:00 2001 From: mesilov Date: Wed, 23 Nov 2022 12:38:23 +0400 Subject: [PATCH 35/94] add OnExternalCallStart Signed-off-by: mesilov --- CHANGELOG.md | 2 + .../Telephony/Requests/Events/Auth.php | 5 +- .../Requests/Events/OnExternalCallStart.php | 95 ------------------- .../Events/OnExternalCallStart/CallData.php | 42 ++++++++ .../OnExternalCallStart.php | 30 ++++++ 5 files changed, 75 insertions(+), 99 deletions(-) delete mode 100644 src/Services/Telephony/Requests/Events/OnExternalCallStart.php create mode 100644 src/Services/Telephony/Requests/Events/OnExternalCallStart/CallData.php create mode 100644 src/Services/Telephony/Requests/Events/OnExternalCallStart/OnExternalCallStart.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f4e99fe..fcdb5b57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ to `Bitrix24\SDK\Services\Telephony\Requests\Events\OnVoximplantCallInit\OnVoximplantCallInit` * from `Bitrix24\SDK\Services\Telephony\Requests\Events\OnVoximplantCallStart` to `Bitrix24\SDK\Services\Telephony\Requests\Events\OnVoximplantCallStart\OnVoximplantCallStart` + * from `Bitrix24\SDK\Services\Telephony\Requests\Events\OnExternalCallStart` + to `Bitrix24\SDK\Services\Telephony\Requests\Events\OnExternalCallStart\OnExternalCallStart` ### Bugfix diff --git a/src/Services/Telephony/Requests/Events/Auth.php b/src/Services/Telephony/Requests/Events/Auth.php index b7c2e04a..a0a3dadd 100644 --- a/src/Services/Telephony/Requests/Events/Auth.php +++ b/src/Services/Telephony/Requests/Events/Auth.php @@ -38,7 +38,4 @@ public function __get($offset) default => parent::__get($offset), }; } -} - - - +} \ No newline at end of file diff --git a/src/Services/Telephony/Requests/Events/OnExternalCallStart.php b/src/Services/Telephony/Requests/Events/OnExternalCallStart.php deleted file mode 100644 index 6361a3c1..00000000 --- a/src/Services/Telephony/Requests/Events/OnExternalCallStart.php +++ /dev/null @@ -1,95 +0,0 @@ -eventPayload['data']['USER_ID']; - } - - /** - * @return string Outbound call ID. - */ - public function getPhoneNumber(): string - { - return $this->eventPayload['data']['PHONE_NUMBER']; - } - - /** - * @return string - */ - public function getPhoneNumberInternational(): string - { - return $this->eventPayload['data']['PHONE_NUMBER_INTERNATIONAL']; - } - - /** - * @return \Bitrix24\SDK\Services\Telephony\Common\CrmEntityType - * @throws \Bitrix24\SDK\Core\Exceptions\InvalidArgumentException - */ - public function getCrmEntityType(): CrmEntityType - { - return CrmEntityType::initByCode($this->eventPayload['data']['CRM_ENTITY_TYPE']); - } - - /** - * @return int The CRM object ID, which type is specified in CRM_ENTITY_TYPE. - */ - public function getCrmEntityId(): int - { - return (int)$this->eventPayload['data']['CRM_ENTITY_ID']; - } - - /** - * @return int Call list ID, if the call is made from the call list. - */ - public function getCallListId(): int - { - return (int)$this->eventPayload['data']['CALL_LIST_ID']; - } - - /** - * @return string External line number, via which the the call is requested. - */ - public function getLineNumber(): string - { - return $this->eventPayload['data']['LINE_NUMBER']; - } - - /** - * @return string Call ID from the telephony.externalcall.register method. - */ - public function getCallId(): string - { - return $this->eventPayload['data']['CALL_ID']; - } - - /** - * @return string - */ - public function getExtension(): string - { - return $this->eventPayload['data']['EXTENSION']; - } - - /** - * @return bool Defines call as initiated from the mobile app. - */ - public function isMobile(): bool - { - return !($this->eventPayload['data']['IS_MOBILE'] === '0'); - } -} \ No newline at end of file diff --git a/src/Services/Telephony/Requests/Events/OnExternalCallStart/CallData.php b/src/Services/Telephony/Requests/Events/OnExternalCallStart/CallData.php new file mode 100644 index 00000000..6a876d0d --- /dev/null +++ b/src/Services/Telephony/Requests/Events/OnExternalCallStart/CallData.php @@ -0,0 +1,42 @@ + (int)$this->data[$offset] !== 0, + 'CALL_TYPE' => CallType::initByTypeCode((int)$this->data[$offset]), + 'CRM_ENTITY_TYPE' => (string)$this->data[$offset], + 'REST_APP_ID', 'CALL_LIST_ID', 'CRM_CREATED_LEAD', 'CRM_ENTITY_ID', 'USER_ID' => (int)$this->data[$offset], + default => parent::__get($offset), + }; + } +} \ No newline at end of file diff --git a/src/Services/Telephony/Requests/Events/OnExternalCallStart/OnExternalCallStart.php b/src/Services/Telephony/Requests/Events/OnExternalCallStart/OnExternalCallStart.php new file mode 100644 index 00000000..a15f1318 --- /dev/null +++ b/src/Services/Telephony/Requests/Events/OnExternalCallStart/OnExternalCallStart.php @@ -0,0 +1,30 @@ +eventPayload['data']); + } + + /** + * @return \Bitrix24\SDK\Services\Telephony\Requests\Events\Auth + */ + public function getAuth(): Auth + { + return new Auth($this->eventPayload['auth']); + } +} \ No newline at end of file From a4152a624e06f7d0bb9c4014baa53b81df2ed68a Mon Sep 17 00:00:00 2001 From: mesilov Date: Wed, 23 Nov 2022 13:31:32 +0400 Subject: [PATCH 36/94] fix data structure in CallData Signed-off-by: mesilov --- .../Requests/Events/OnVoximplantCallInit/CallData.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Services/Telephony/Requests/Events/OnVoximplantCallInit/CallData.php b/src/Services/Telephony/Requests/Events/OnVoximplantCallInit/CallData.php index 565ed6d7..383db9bb 100644 --- a/src/Services/Telephony/Requests/Events/OnVoximplantCallInit/CallData.php +++ b/src/Services/Telephony/Requests/Events/OnVoximplantCallInit/CallData.php @@ -8,10 +8,13 @@ use Bitrix24\SDK\Services\Telephony\Common\CallType; /** - * @property-read string $CALL_ID - * @property-read string $CALLER_ID - * @property-read int $REST_APP_ID - * @property-read CallType $CALL_TYPE + * @property-read string|null $ACCOUNT_SEARCH_ID Line ID (numeric for leased PBX, regXXX for cloud hosted PBX, and sipXXX for office PBX). + * @property-read string $CALL_ID Call identifier. + * @property-read string|null $PHONE_NUMBER Number called by the operator (if call type is: 1 – Outbound) or number called by the subscriber (if call type is: 2 – Inbound). + * @property-read string $CALLER_ID Line identifier (if call type is: 1 – Outbound) or telephone number used to make a call to the portal (if call type is: 2 – Inbound). + * @property-read int $REST_APP_ID + * @property-read CallType $CALL_TYPE Call type (see Call Type Description). https://training.bitrix24.com/rest_help/scope_telephony/codes_and_types.php#call_type + * @link https://training.bitrix24.com/rest_help/scope_telephony/voximplant/events/onvoximplantcallInit.php */ class CallData extends AbstractItem { From 6a13fee873f4acb4c760706b431aeb77c1dbaa50 Mon Sep 17 00:00:00 2001 From: mesilov Date: Wed, 23 Nov 2022 13:54:23 +0400 Subject: [PATCH 37/94] add OnVoximplantCallEnd event Signed-off-by: mesilov --- CHANGELOG.md | 2 + .../Requests/Events/OnVoximplantCallEnd.php | 114 ------------------ .../Events/OnVoximplantCallEnd/CallData.php | 66 ++++++++++ .../OnVoximplantCallEnd.php | 35 ++++++ 4 files changed, 103 insertions(+), 114 deletions(-) delete mode 100644 src/Services/Telephony/Requests/Events/OnVoximplantCallEnd.php create mode 100644 src/Services/Telephony/Requests/Events/OnVoximplantCallEnd/CallData.php create mode 100644 src/Services/Telephony/Requests/Events/OnVoximplantCallEnd/OnVoximplantCallEnd.php diff --git a/CHANGELOG.md b/CHANGELOG.md index fcdb5b57..77d49a4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ to `Bitrix24\SDK\Services\Telephony\Requests\Events\OnVoximplantCallStart\OnVoximplantCallStart` * from `Bitrix24\SDK\Services\Telephony\Requests\Events\OnExternalCallStart` to `Bitrix24\SDK\Services\Telephony\Requests\Events\OnExternalCallStart\OnExternalCallStart` + * from `Bitrix24\SDK\Services\Telephony\Requests\Events\OnVoximplantCallEnd` + to `Bitrix24\SDK\Services\Telephony\Requests\Events\OnVoximplantCallEnd\OnVoximplantCallEnd` ### Bugfix diff --git a/src/Services/Telephony/Requests/Events/OnVoximplantCallEnd.php b/src/Services/Telephony/Requests/Events/OnVoximplantCallEnd.php deleted file mode 100644 index ff0229f4..00000000 --- a/src/Services/Telephony/Requests/Events/OnVoximplantCallEnd.php +++ /dev/null @@ -1,114 +0,0 @@ -eventPayload['data']['CALL_ID']; - } - - /** - * @throws \Bitrix24\SDK\Core\Exceptions\InvalidArgumentException - */ - public function getCallType(): CallType - { - return CallType::initByTypeCode((int)$this->eventPayload['data']['CALL_TYPE']); - } - - /** - * @return string Number used by the subscriber to make a call (if call type is: 2 – Inbound) or number called by the operator (if call type is: 1 – Outbound). - */ - public function getPhoneNumber(): string - { - return $this->eventPayload['data']['PHONE_NUMBER']; - } - - /** - * @return string Number receiving the call (if call type is: 2 – Inbound) or number from which the call was made (if call type is: 1 – Outbound). - */ - public function getPortalNumber(): string - { - return $this->eventPayload['data']['PORTAL_NUMBER']; - } - - /** - * @return int Responding operator ID (if call type is: 2 – Inbound) or identifier of the calling operator (if call type is: 1 – Outbound). - */ - public function getPortalUserId(): int - { - return (int)$this->eventPayload['data']['PORTAL_USER_ID']; - } - - /** - * @return int Call duration. - */ - public function getCallDuration(): int - { - return (int)$this->eventPayload['data']['CALL_DURATION']; - } - - /** - * @return \DateTimeImmutable Date in ISO format. - */ - public function getCallStartDate(): DateTimeImmutable - { - return DateTimeImmutable::createFromFormat(DATE_ATOM, $this->eventPayload['data']['CALL_START_DATE']); - } - - /** - * @return \Money\Money Call cost. - */ - public function getCost(): Money - { - if ($this->eventPayload['COST'] === '') { - return new Money(0, new Currency($this->eventPayload['data']['COST_CURRENCY'])); - } - - return (new DecimalMoneyParser(new ISOCurrencies()))->parse( - $this->eventPayload['data']['COST'], - $this->eventPayload['data']['COST_CURRENCY'] - ); - } - - /** - * @return int Call code (See Call Code Table). - */ - public function getCallFailedCode(): int - { - return (int)$this->eventPayload['data']['CALL_FAILED_CODE']; - } - - /** - * @return string Call code textual description (Latin letters). - */ - public function getCallFailedReason(): string - { - return $this->eventPayload['data']['CALL_FAILED_REASON']; - } - - /** - * @return int - */ - public function getCrmActivityId(): int - { - return (int)$this->eventPayload['data']['CRM_ACTIVITY_ID']; - } -} \ No newline at end of file diff --git a/src/Services/Telephony/Requests/Events/OnVoximplantCallEnd/CallData.php b/src/Services/Telephony/Requests/Events/OnVoximplantCallEnd/CallData.php new file mode 100644 index 00000000..de70f004 --- /dev/null +++ b/src/Services/Telephony/Requests/Events/OnVoximplantCallEnd/CallData.php @@ -0,0 +1,66 @@ +data[$offset]); + case 'CALL_TYPE': + return CallType::initByTypeCode((int)$this->data[$offset]); + case 'CALL_DURATION': + case 'CALL_FAILED_CODE': + case 'CRM_ACTIVITY_ID': + case 'PORTAL_USER_ID': + return (int)$this->data[$offset]; + case 'COST_CURRENCY': + return new Currency($this->data[$offset]); + case 'COST': + if ($this->data[$offset] === null) { + return new Money(0, new Currency($this->data['COST_CURRENCY'])); + } + + return (new DecimalMoneyParser(new ISOCurrencies()))->parse( + $this->data[$offset], + new Currency($this->data['COST_CURRENCY']) + ); + default: + parent::__get($offset); + } + } +} \ No newline at end of file diff --git a/src/Services/Telephony/Requests/Events/OnVoximplantCallEnd/OnVoximplantCallEnd.php b/src/Services/Telephony/Requests/Events/OnVoximplantCallEnd/OnVoximplantCallEnd.php new file mode 100644 index 00000000..1b0697ad --- /dev/null +++ b/src/Services/Telephony/Requests/Events/OnVoximplantCallEnd/OnVoximplantCallEnd.php @@ -0,0 +1,35 @@ +eventPayload['auth']); + } + + /** + * @return \Bitrix24\SDK\Services\Telephony\Requests\Events\OnVoximplantCallEnd\CallData + */ + public function getCallData(): CallData + { + return new CallData($this->eventPayload['data']); + } +} \ No newline at end of file From b41b1772e7874fc8f3d23d7e50559d4a9dd206b5 Mon Sep 17 00:00:00 2001 From: mesilov Date: Wed, 23 Nov 2022 20:05:08 +0400 Subject: [PATCH 38/94] add OnVoximplantCallEnd event Signed-off-by: mesilov --- .../Telephony/Requests/Events/OnVoximplantCallEnd/CallData.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Services/Telephony/Requests/Events/OnVoximplantCallEnd/CallData.php b/src/Services/Telephony/Requests/Events/OnVoximplantCallEnd/CallData.php index de70f004..c3a4cc1d 100644 --- a/src/Services/Telephony/Requests/Events/OnVoximplantCallEnd/CallData.php +++ b/src/Services/Telephony/Requests/Events/OnVoximplantCallEnd/CallData.php @@ -39,6 +39,8 @@ class CallData extends AbstractItem public function __get($offset) { switch ($offset) { + case 'CALL_ID': + return (string)$this->data[$offset]; case 'CALL_START_DATE': return new \DateTimeImmutable((string)$this->data[$offset]); case 'CALL_TYPE': From e2aa435cf438fc26c7bc24c35b516abefec9515e Mon Sep 17 00:00:00 2001 From: mesilov Date: Wed, 23 Nov 2022 20:09:17 +0400 Subject: [PATCH 39/94] fix OnVoximplantCallEnd event Signed-off-by: mesilov --- .../Telephony/Requests/Events/OnVoximplantCallEnd/CallData.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Services/Telephony/Requests/Events/OnVoximplantCallEnd/CallData.php b/src/Services/Telephony/Requests/Events/OnVoximplantCallEnd/CallData.php index c3a4cc1d..b8ddfbd0 100644 --- a/src/Services/Telephony/Requests/Events/OnVoximplantCallEnd/CallData.php +++ b/src/Services/Telephony/Requests/Events/OnVoximplantCallEnd/CallData.php @@ -62,7 +62,7 @@ public function __get($offset) new Currency($this->data['COST_CURRENCY']) ); default: - parent::__get($offset); + return parent::__get($offset); } } } \ No newline at end of file From 43f0f3afb98cc2d99d2e7d6dbeb4257da6f053cf Mon Sep 17 00:00:00 2001 From: mesilov Date: Mon, 30 Jan 2023 17:30:02 +0400 Subject: [PATCH 40/94] bump php-version Signed-off-by: mesilov --- CHANGELOG.md | 1 + composer.json | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77d49a4c..ea66c7ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * add [service builder factory](https://github.com/mesilov/bitrix24-php-sdk/issues/328) * add method `Bitrix24\SDK\Core\Credentials\Scope::initFromString` * add method `Bitrix24\SDK\Application\ApplicationStatus::initFromString` +* ❗️add php 8.2 support ### Changed diff --git a/composer.json b/composer.json index 82df0352..e2dc8095 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ } ], "require": { - "php": "8.1.*", + "php": "8.1.* || 8.2.*", "ext-json": "*", "ext-bcmath": "*", "ext-curl": "*", @@ -33,7 +33,7 @@ "symfony/event-dispatcher": "5.4.* || 6.*", "ramsey/uuid": "^4.2.3", "moneyphp/money": "3.* || 4.*", - "symfony/uid": "^6.0", + "symfony/uid": "6.*", "ext-intl": "*" }, "require-dev": { @@ -44,8 +44,7 @@ "phpstan/phpstan": "1.*", "phpunit/phpunit": "9.5.*", "symfony/stopwatch": "5.4.* || 6.*", - "roave/security-advisories": "dev-master", - "ext-intl": "*" + "roave/security-advisories": "dev-master" }, "autoload": { "psr-4": { From 1be62914d99487cf1d3f99772029d5dd49290b0b Mon Sep 17 00:00:00 2001 From: mesilov Date: Fri, 17 Feb 2023 16:49:58 +0400 Subject: [PATCH 41/94] bump php-version in ci Signed-off-by: mesilov --- .github/workflows/integration.yml | 3 +-- .github/workflows/phpstan.yml | 4 ++-- .github/workflows/phpunit.yml | 3 ++- .github/workflows/vendor-check.yml | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 0137cbe9..bb8210c7 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: php-version: - - "7.4" + - "8.1" dependencies: [ highest ] steps: @@ -36,7 +36,6 @@ jobs: ini-values: variables_order=EGPCS env: BITRIX24_PHP_SDK_PLAYGROUND_WEBHOOK: ${{ secrets.BITRIX24_PHP_SDK_PLAYGROUND_WEBHOOK }} - TEST2_ENV: 12345 - name: "Install dependencies" run: | diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index ea58e758..d2826503 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -13,8 +13,8 @@ jobs: fail-fast: false matrix: php-version: - - "7.4" - - "8.0" + - "8.1" + - "8.2" dependencies: - "lowest" - "highest" diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 725bb64a..e1512640 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -16,7 +16,8 @@ jobs: strategy: matrix: php-version: - - "7.4" + - "8.1" + - "8.2" dependencies: [ highest ] steps: diff --git a/.github/workflows/vendor-check.yml b/.github/workflows/vendor-check.yml index 802c6d45..beaa80f5 100644 --- a/.github/workflows/vendor-check.yml +++ b/.github/workflows/vendor-check.yml @@ -20,6 +20,7 @@ jobs: matrix: php-version: - "8.1" + - "8.2" dependencies: [ highest ] steps: From c939de946451498ba95d57b21539ba8ee83332e8 Mon Sep 17 00:00:00 2001 From: mesilov Date: Fri, 17 Feb 2023 17:20:44 +0400 Subject: [PATCH 42/94] fix phpdoc Signed-off-by: mesilov --- src/Application/ApplicationStatus.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Application/ApplicationStatus.php b/src/Application/ApplicationStatus.php index 3fefdbc8..fc65ebc4 100644 --- a/src/Application/ApplicationStatus.php +++ b/src/Application/ApplicationStatus.php @@ -20,7 +20,7 @@ class ApplicationStatus /** * @param string $statusShortCode * - * @throws \Bitrix24\SDK\Core\Exceptions\InvalidArgumentException + * @throws InvalidArgumentException */ public function __construct(string $statusShortCode) { @@ -107,10 +107,10 @@ public function getStatusCode(): string } /** - * @param \Symfony\Component\HttpFoundation\Request $request + * @param Request $request * * @return self - * @throws \Bitrix24\SDK\Core\Exceptions\InvalidArgumentException + * @throws InvalidArgumentException */ public static function initFromRequest(Request $request): self { @@ -120,8 +120,8 @@ public static function initFromRequest(Request $request): self /** * @param string $shortStatusCode * - * @return static - * @throws \Bitrix24\SDK\Core\Exceptions\InvalidArgumentException + * @return self + * @throws InvalidArgumentException */ public static function initFromString(string $shortStatusCode): self { From fc98eb51a77d33c6f8257417e86fcb6ef4fd4806 Mon Sep 17 00:00:00 2001 From: mesilov Date: Fri, 17 Feb 2023 17:27:24 +0400 Subject: [PATCH 43/94] fix undefined index error Signed-off-by: mesilov --- src/Core/ApiClient.php | 46 +++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/Core/ApiClient.php b/src/Core/ApiClient.php index 46ab0497..ac7387e0 100644 --- a/src/Core/ApiClient.php +++ b/src/Core/ApiClient.php @@ -6,7 +6,9 @@ use Bitrix24\SDK\Core\Contracts\ApiClientInterface; use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; use Bitrix24\SDK\Core\Response\DTO\RenewedAccessToken; +use Fig\Http\Message\StatusCodeInterface; use Psr\Log\LoggerInterface; use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -31,8 +33,8 @@ class ApiClient implements ApiClientInterface * ApiClient constructor. * * @param Credentials\Credentials $credentials - * @param HttpClientInterface $client - * @param LoggerInterface $logger + * @param HttpClientInterface $client + * @param LoggerInterface $logger */ public function __construct(Credentials\Credentials $credentials, HttpClientInterface $client, LoggerInterface $logger) { @@ -53,11 +55,11 @@ public function __construct(Credentials\Credentials $credentials, HttpClientInte protected function getDefaultHeaders(): array { return [ - 'Accept' => 'application/json', - 'Accept-Charset' => 'utf-8', - 'User-Agent' => sprintf('%s-v-%s-php-%s', self::SDK_USER_AGENT, self::SDK_VERSION, PHP_VERSION), + 'Accept' => 'application/json', + 'Accept-Charset' => 'utf-8', + 'User-Agent' => sprintf('%s-v-%s-php-%s', self::SDK_USER_AGENT, self::SDK_VERSION, PHP_VERSION), 'X-BITRIX24-PHP-SDK-PHP-VERSION' => PHP_VERSION, - 'X-BITRIX24-PHP-SDK-VERSION' => self::SDK_VERSION, + 'X-BITRIX24-PHP-SDK-VERSION' => self::SDK_VERSION, ]; } @@ -74,6 +76,7 @@ public function getCredentials(): Credentials\Credentials * @throws InvalidArgumentException * @throws TransportExceptionInterface * @throws \JsonException + * @throws TransportException */ public function getNewAccessToken(): RenewedAccessToken { @@ -91,8 +94,8 @@ public function getNewAccessToken(): RenewedAccessToken $this::BITRIX24_OAUTH_SERVER_URL, http_build_query( [ - 'grant_type' => 'refresh_token', - 'client_id' => $this->getCredentials()->getApplicationProfile()->getClientId(), + 'grant_type' => 'refresh_token', + 'client_id' => $this->getCredentials()->getApplicationProfile()->getClientId(), 'client_secret' => $this->getCredentials()->getApplicationProfile()->getClientSecret(), 'refresh_token' => $this->getCredentials()->getAccessToken()->getRefreshToken(), ] @@ -103,16 +106,21 @@ public function getNewAccessToken(): RenewedAccessToken 'headers' => $this->getDefaultHeaders(), ]; $response = $this->client->request($method, $url, $requestOptions); - $result = $response->toArray(false); - $newAccessToken = RenewedAccessToken::initFromArray($result); + $responseData = $response->toArray(false); + if ($response->getStatusCode() === StatusCodeInterface::STATUS_OK) { + $newAccessToken = RenewedAccessToken::initFromArray($responseData); - $this->logger->debug('getNewAccessToken.finish'); - - return $newAccessToken; + $this->logger->debug('getNewAccessToken.finish'); + return $newAccessToken; + } + if ($response->getStatusCode() === StatusCodeInterface::STATUS_BAD_REQUEST) { + throw new TransportException(sprintf('getting new access token failure: %s', $responseData['error'])); + } + throw new TransportException('getting new access token failure with unknown http-status code %s', $response->getStatusCode()); } /** - * @param string $apiMethod + * @param string $apiMethod * @param array $parameters * * @return ResponseInterface @@ -124,8 +132,8 @@ public function getResponse(string $apiMethod, array $parameters = []): Response $this->logger->info( 'getResponse.start', [ - 'apiMethod' => $apiMethod, - 'domainUrl' => $this->credentials->getDomainUrl(), + 'apiMethod' => $apiMethod, + 'domainUrl' => $this->credentials->getDomainUrl(), 'parameters' => $parameters, ] ); @@ -143,8 +151,8 @@ public function getResponse(string $apiMethod, array $parameters = []): Response } $requestOptions = [ - 'json' => $parameters, - 'headers' => $this->getDefaultHeaders(), + 'json' => $parameters, + 'headers' => $this->getDefaultHeaders(), // disable redirects, try to catch portal change domain name event 'max_redirects' => 0, ]; @@ -153,7 +161,7 @@ public function getResponse(string $apiMethod, array $parameters = []): Response $this->logger->info( 'getResponse.end', [ - 'apiMethod' => $apiMethod, + 'apiMethod' => $apiMethod, 'responseInfo' => $response->getInfo(), ] ); From 5afb67ba4e63acc95449ecce5817ebfd13f98322 Mon Sep 17 00:00:00 2001 From: mesilov Date: Fri, 17 Feb 2023 17:31:21 +0400 Subject: [PATCH 44/94] remove phpstan check on lowest versions Signed-off-by: mesilov --- .github/workflows/phpstan.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index d2826503..068e9864 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -15,9 +15,7 @@ jobs: php-version: - "8.1" - "8.2" - dependencies: - - "lowest" - - "highest" + dependencies: [ highest ] steps: - name: "Checkout" From 4ef8b63b21014a4591bc854e555e8110464d7a6c Mon Sep 17 00:00:00 2001 From: mesilov Date: Fri, 17 Feb 2023 21:44:17 +0400 Subject: [PATCH 45/94] add method isError Signed-off-by: mesilov --- CHANGELOG.md | 5 +++-- .../Result/ExternalCallRegisterItemResult.php | 14 +++++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea66c7ca..355578df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # bitrix24-php-sdk change log -## 2.0-beta.1 — 10.11.2022 +## 2.0-beta.1 — 25.02.2023 ### Added @@ -30,7 +30,8 @@ * fix [typehint at ContactItemResult](https://github.com/mesilov/bitrix24-php-sdk/issues/320) * fix [return types in DealCategoryItemResult](https://github.com/mesilov/bitrix24-php-sdk/issues/322) * fix [add auth node in telephony voximplant events requests](https://github.com/mesilov/bitrix24-php-sdk/issues/331) - +* fix [add helper metods isError for registerCallResult fortelephony](https://github.com/mesilov/bitrix24-php-sdk/issues/335) +* ### etc ## 2.0-alpha.7 — 8.08.2022 diff --git a/src/Services/Telephony/Result/ExternalCallRegisterItemResult.php b/src/Services/Telephony/Result/ExternalCallRegisterItemResult.php index ba8a09c2..ef2759a1 100644 --- a/src/Services/Telephony/Result/ExternalCallRegisterItemResult.php +++ b/src/Services/Telephony/Result/ExternalCallRegisterItemResult.php @@ -16,14 +16,22 @@ use Bitrix24\SDK\Core\Result\AbstractItem; /** + * If registration of the call was unsuccessful, the LEAD_CREATION_ERROR field will contain the error message. + * * @property-read string $CALL_ID - * @property-read int $CRM_CREATED_LEAD - * @property-read int $CRM_ENTITY_ID + * @property-read ?int $CRM_CREATED_LEAD + * @property-read ?int $CRM_ENTITY_ID * @property-read string $CRM_ENTITY_TYPE * @property-read array $CRM_CREATED_ENTITIES * @property-read string $LEAD_CREATION_ERROR */ class ExternalCallRegisterItemResult extends AbstractItem { - + /** + * @return bool + */ + public function isError(): bool + { + return $this->data['LEAD_CREATION_ERROR'] !== '' && $this->data['LEAD_CREATION_ERROR'] !== null; + } } \ No newline at end of file From 0fe12f11bbbd9a5672153879650cc127cf22d9d6 Mon Sep 17 00:00:00 2001 From: mesilov Date: Sat, 18 Feb 2023 00:00:09 +0400 Subject: [PATCH 46/94] add method isError - fix Signed-off-by: mesilov --- .../Telephony/Result/ExternalCallRegisterItemResult.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Services/Telephony/Result/ExternalCallRegisterItemResult.php b/src/Services/Telephony/Result/ExternalCallRegisterItemResult.php index ef2759a1..9b4796d5 100644 --- a/src/Services/Telephony/Result/ExternalCallRegisterItemResult.php +++ b/src/Services/Telephony/Result/ExternalCallRegisterItemResult.php @@ -32,6 +32,9 @@ class ExternalCallRegisterItemResult extends AbstractItem */ public function isError(): bool { + if (!$this->isKeyExists('LEAD_CREATION_ERROR')) { + return false; + } return $this->data['LEAD_CREATION_ERROR'] !== '' && $this->data['LEAD_CREATION_ERROR'] !== null; } } \ No newline at end of file From 5e0fa4778b6ade4700469b9dc37aed5fc734a1fa Mon Sep 17 00:00:00 2001 From: mesilov Date: Wed, 15 Mar 2023 01:48:19 +0400 Subject: [PATCH 47/94] fix non exists field operating on outdated portals Signed-off-by: mesilov --- src/Core/Response/DTO/Time.php | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/Core/Response/DTO/Time.php b/src/Core/Response/DTO/Time.php index e1302aa1..3e83f84b 100644 --- a/src/Core/Response/DTO/Time.php +++ b/src/Core/Response/DTO/Time.php @@ -33,25 +33,26 @@ class Time /** * Time constructor. * - * @param float $start - * @param float $finish - * @param float $duration - * @param float $processing - * @param float $operating + * @param float $start + * @param float $finish + * @param float $duration + * @param float $processing + * @param float $operating * @param \DateTimeImmutable $dateStart * @param \DateTimeImmutable $dateFinish - * @param int|null $operatingResetAt + * @param int|null $operatingResetAt */ public function __construct( - float $start, - float $finish, - float $duration, - float $processing, - float $operating, + float $start, + float $finish, + float $duration, + float $processing, + float $operating, DateTimeImmutable $dateStart, DateTimeImmutable $dateFinish, - ?int $operatingResetAt - ) { + ?int $operatingResetAt + ) + { $this->start = $start; $this->finish = $finish; $this->duration = $duration; @@ -139,7 +140,7 @@ public static function initFromResponse(array $response): self (float)$response['finish'], (float)$response['duration'], (float)$response['processing'], - (float)$response['operating'], + array_key_exists('operating', $response) ? (float)$response['operating'] : 0, new DateTimeImmutable($response['date_start']), new DateTimeImmutable($response['date_finish']), $response['operating_reset_at'] ?? null From a9157d71e79a7888db459ed93e641a292c794191 Mon Sep 17 00:00:00 2001 From: mesilov Date: Wed, 15 Mar 2023 01:54:53 +0400 Subject: [PATCH 48/94] add crm multi-field type Phone Signed-off-by: mesilov --- .../CRM/Common/Result/AbstractCrmItem.php | 37 +++++--- .../Result/SystemFields/Types/Phone.php | 25 +++++ .../SystemFields/Types/PhoneValueType.php | 16 ++++ .../CRM/Contact/Result/ContactItemResult.php | 6 +- src/Services/CRM/Contact/Service/Contact.php | 2 +- .../Services/CRM/PhoneCollectionBuilder.php | 92 +++++++++++++++++++ .../Services/CRM/PhoneNumberBuilder.php | 31 +++++++ 7 files changed, 193 insertions(+), 16 deletions(-) create mode 100644 src/Services/CRM/Common/Result/SystemFields/Types/Phone.php create mode 100644 src/Services/CRM/Common/Result/SystemFields/Types/PhoneValueType.php create mode 100644 tests/Builders/Services/CRM/PhoneCollectionBuilder.php create mode 100644 tests/Builders/Services/CRM/PhoneNumberBuilder.php diff --git a/src/Services/CRM/Common/Result/AbstractCrmItem.php b/src/Services/CRM/Common/Result/AbstractCrmItem.php index c05d6984..05e5f304 100644 --- a/src/Services/CRM/Common/Result/AbstractCrmItem.php +++ b/src/Services/CRM/Common/Result/AbstractCrmItem.php @@ -5,6 +5,8 @@ namespace Bitrix24\SDK\Services\CRM\Common\Result; use Bitrix24\SDK\Core\Result\AbstractItem; +use Bitrix24\SDK\Services\CRM\Common\Result\SystemFields\Types\Phone; +use Bitrix24\SDK\Services\CRM\Common\Result\SystemFields\Types\PhoneValueType; use Bitrix24\SDK\Services\CRM\Userfield\Exceptions\UserfieldNotFoundException; use DateTimeImmutable; use Money\Currency; @@ -15,7 +17,7 @@ class AbstractCrmItem extends AbstractItem private const CRM_USERFIELD_PREFIX = 'UF_CRM_'; /** - * @var \Money\Currency + * @var Currency */ private Currency $currency; @@ -25,7 +27,6 @@ public function __construct(array $data, Currency $currency = null) if ($currency !== null) { $this->currency = $currency; } - } /** @@ -67,16 +68,6 @@ public function __get($offset) case 'HAS_EMAIL': case 'HAS_IMOL': case 'OPENED': - // deal - case 'PRICE_EXCLUSIVE': - case 'PRICE_NETTO': - case 'PRICE_BRUTTO': - case 'PRICE': - if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { - $var = $this->data[$offset] * 100; - return new Money((string)$var, new Currency($this->currency->getCode())); - } - return null; case 'IS_MANUAL_OPPORTUNITY': case 'CLOSED': case 'IS_NEW': @@ -96,6 +87,26 @@ public function __get($offset) } return null; + // deal + case 'PRICE_EXCLUSIVE': + case 'PRICE_NETTO': + case 'PRICE_BRUTTO': + case 'PRICE': + if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { + $var = $this->data[$offset] * 100; + return new Money((string)$var, new Currency($this->currency->getCode())); + } + return null; + case 'PHONE': + if (!$this->isKeyExists($offset)) { + return []; + } + + $items = []; + foreach ($this->data[$offset] as $phone) { + $items[] = new Phone($phone); + } + return $items; default: return $this->data[$offset] ?? null; } @@ -107,7 +118,7 @@ public function __get($offset) * @param string $fieldName * * @return mixed|null - * @throws \Bitrix24\SDK\Services\CRM\Userfield\Exceptions\UserfieldNotFoundException + * @throws UserfieldNotFoundException */ protected function getKeyWithUserfieldByFieldName(string $fieldName) { diff --git a/src/Services/CRM/Common/Result/SystemFields/Types/Phone.php b/src/Services/CRM/Common/Result/SystemFields/Types/Phone.php new file mode 100644 index 00000000..73763cba --- /dev/null +++ b/src/Services/CRM/Common/Result/SystemFields/Types/Phone.php @@ -0,0 +1,25 @@ + $this->data[$offset], + 'ID' => (int)$this->data['ID'], + 'VALUE_TYPE' => PhoneValueType::from($this->data['VALUE_TYPE']), + default => parent::__get($offset), + }; + } +} \ No newline at end of file diff --git a/src/Services/CRM/Common/Result/SystemFields/Types/PhoneValueType.php b/src/Services/CRM/Common/Result/SystemFields/Types/PhoneValueType.php new file mode 100644 index 00000000..8d9636b4 --- /dev/null +++ b/src/Services/CRM/Common/Result/SystemFields/Types/PhoneValueType.php @@ -0,0 +1,16 @@ + 'N']): AddedItemResult { return new AddedItemResult( - $result = $this->core->call( + $this->core->call( 'crm.contact.add', [ 'fields' => $fields, diff --git a/tests/Builders/Services/CRM/PhoneCollectionBuilder.php b/tests/Builders/Services/CRM/PhoneCollectionBuilder.php new file mode 100644 index 00000000..2dbe6819 --- /dev/null +++ b/tests/Builders/Services/CRM/PhoneCollectionBuilder.php @@ -0,0 +1,92 @@ +phones = []; + $this->phoneNumberBuilder = new PhoneNumberBuilder(); + } + + /** + * @throws Exception + */ + public function withDuplicatePhones(int $duplicatesCount = 1): self + { + $duplicatePhone = $this->phoneNumberBuilder->build(); + + $duplicates = []; + for ($i = 0; $i <= $duplicatesCount; $i++) { + $duplicates[] = new Phone([ + 'ID' => time() + random_int(1, 1000000), + 'VALUE' => $duplicatePhone, + 'VALUE_TYPE' => PhoneValueType::work->value + ]); + } + + $this->phones = array_merge( + [ + new Phone([ + 'ID' => time() + random_int(1, 1000000), + 'VALUE' => $this->phoneNumberBuilder->build(), + 'VALUE_TYPE' => PhoneValueType::work->value + ]) + ], + $duplicates + ); + + return $this; + } + + public function withPhoneLength(int $phoneLength): self + { + $this->phones = [ + new Phone([ + 'ID' => time() + random_int(1, 1000000), + 'VALUE' => $this->phoneNumberBuilder->withLength($phoneLength)->build(), + 'VALUE_TYPE' => PhoneValueType::work->value + ]), + new Phone([ + 'ID' => time() + random_int(1, 1000000), + 'VALUE' => $this->phoneNumberBuilder->withLength(7)->build(), + 'VALUE_TYPE' => PhoneValueType::work->value + ]) + ]; + + return $this; + } + + /** + * @throws Exception + */ + public function build(): array + { + return $this->phones; + } + + public function buildNewPhonesCommand(): array + { + $res = []; + foreach ($this->phones as $phone) { + $res[] = [ + 'VALUE' => $phone->VALUE, + 'VALUE_TYPE' => $phone->VALUE_TYPE->value + ]; + } + return $res; + } +} \ No newline at end of file diff --git a/tests/Builders/Services/CRM/PhoneNumberBuilder.php b/tests/Builders/Services/CRM/PhoneNumberBuilder.php new file mode 100644 index 00000000..bcb14a96 --- /dev/null +++ b/tests/Builders/Services/CRM/PhoneNumberBuilder.php @@ -0,0 +1,31 @@ +length = 7; + } + + public function withLength(int $length): self + { + $this->length = $length; + return $this; + } + + /** + * @throws Exception + */ + public function build(): string + { + return '+7' . substr((string)time(), 2, $this->length) . substr((string)random_int(1000, PHP_INT_MAX), 0, 3); + } +} \ No newline at end of file From 5a87e111a855257d32f3d0aa843859d55ba1587f Mon Sep 17 00:00:00 2001 From: mesilov Date: Fri, 17 Mar 2023 13:43:33 +0400 Subject: [PATCH 49/94] add users scope Signed-off-by: mesilov --- CHANGELOG.md | 12 +- .../CRM/Common/Result/AbstractCrmItem.php | 6 + src/Services/ServiceBuilder.php | 15 +- src/Services/User/Result/UserItemResult.php | 60 ++++++++ src/Services/User/Result/UserResult.php | 16 +++ src/Services/User/Result/UsersResult.php | 27 ++++ src/Services/User/Service/User.php | 120 ++++++++++++++++ src/Services/User/UserServiceBuilder.php | 25 ++++ .../Services/User/Service/UserTest.php | 132 ++++++++++++++++++ 9 files changed, 411 insertions(+), 2 deletions(-) create mode 100644 src/Services/User/Result/UserItemResult.php create mode 100644 src/Services/User/Result/UserResult.php create mode 100644 src/Services/User/Result/UsersResult.php create mode 100644 src/Services/User/Service/User.php create mode 100644 src/Services/User/UserServiceBuilder.php create mode 100644 tests/Integration/Services/User/Service/UserTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 355578df..94600c47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,14 @@ * add method `Bitrix24\SDK\Core\Credentials\Scope::initFromString` * add method `Bitrix24\SDK\Application\ApplicationStatus::initFromString` * ❗️add php 8.2 support +* add system CRM multi-field type `Bitrix24\SDK\Services\CRM\Common\Result\SystemFields\Types\Phone` +* add scope `user`,`user_basic`,`user_brief`,`user.userfield` and services [add scope user support](https://github.com/mesilov/bitrix24-php-sdk/issues/339) + * `Bitrix24\SDK\Services\User\Service\User::fields` - get user fields + * `Bitrix24\SDK\Services\User\Service\User::current` - get current user + * `Bitrix24\SDK\Services\User\Service\User::add` - add user + * `Bitrix24\SDK\Services\User\Service\User::get` - get user + * `Bitrix24\SDK\Services\User\Service\User::update` - update user + * `Bitrix24\SDK\Services\User\Service\User::search` - search users ### Changed @@ -31,7 +39,9 @@ * fix [return types in DealCategoryItemResult](https://github.com/mesilov/bitrix24-php-sdk/issues/322) * fix [add auth node in telephony voximplant events requests](https://github.com/mesilov/bitrix24-php-sdk/issues/331) * fix [add helper metods isError for registerCallResult fortelephony](https://github.com/mesilov/bitrix24-php-sdk/issues/335) -* +* fix [add return type for crm multifields phone, email, im](https://github.com/mesilov/bitrix24-php-sdk/issues/338) +* fix errors in `ShowFieldsDescriptionCommand` metadata reader CLI command + ### etc ## 2.0-alpha.7 — 8.08.2022 diff --git a/src/Services/CRM/Common/Result/AbstractCrmItem.php b/src/Services/CRM/Common/Result/AbstractCrmItem.php index c05d6984..eaf28aa5 100644 --- a/src/Services/CRM/Common/Result/AbstractCrmItem.php +++ b/src/Services/CRM/Common/Result/AbstractCrmItem.php @@ -37,6 +37,12 @@ public function __construct(array $data, Currency $currency = null) public function __get($offset) { // todo унести в отдельный класс и покрыть тестами + // учитывать требования + // - поддержка пользовательских полей с пользовательскими типами + // - поддержка пользовательских полей со встроенными типами + // - расширяемость для пользовательских полей в клиентском коде + // - хранение связи поле-тип в аннотациях? + // приведение полей к реальным типам данных для основных сущностей CRM switch ($offset) { case 'ID': diff --git a/src/Services/ServiceBuilder.php b/src/Services/ServiceBuilder.php index ecc0cac0..e603ce1f 100644 --- a/src/Services/ServiceBuilder.php +++ b/src/Services/ServiceBuilder.php @@ -9,6 +9,7 @@ use Bitrix24\SDK\Services\IMOpenLines\IMOpenLinesServiceBuilder; use Bitrix24\SDK\Services\Main\MainServiceBuilder; use Bitrix24\SDK\Services\Telephony\TelephonyServiceBuilder; +use Bitrix24\SDK\Services\User\UserServiceBuilder; use Bitrix24\SDK\Services\UserConsent\UserConsentServiceBuilder; use Bitrix24\SDK\Services\Placement\PlacementServiceBuilder; @@ -68,7 +69,7 @@ public function getMainScope(): MainServiceBuilder } /** - * @return \Bitrix24\SDK\Services\UserConsent\UserConsentServiceBuilder + * @return UserConsentServiceBuilder */ public function getUserConsentScope(): UserConsentServiceBuilder { @@ -79,6 +80,18 @@ public function getUserConsentScope(): UserConsentServiceBuilder return $this->serviceCache[__METHOD__]; } + /** + * @return UserServiceBuilder + */ + public function getUserScope(): UserServiceBuilder + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new UserServiceBuilder($this->core, $this->batch, $this->bulkItemsReader, $this->log); + } + + return $this->serviceCache[__METHOD__]; + } + /** * @return PlacementServiceBuilder */ diff --git a/src/Services/User/Result/UserItemResult.php b/src/Services/User/Result/UserItemResult.php new file mode 100644 index 00000000..e8ad00e0 --- /dev/null +++ b/src/Services/User/Result/UserItemResult.php @@ -0,0 +1,60 @@ +data[$offset]; + case 'LAST_LOGIN': + case 'DATE_REGISTER': + case 'UF_EMPLOYMENT_DATE': + if ($this->data[$offset] !== '') { + return DateTimeImmutable::createFromFormat(DATE_ATOM, $this->data[$offset]); + } + break; + case 'IS_ONLINE': + return $this->data[$offset] === 'Y'; + } + + return $this->data[$offset] ?? null; + } +} \ No newline at end of file diff --git a/src/Services/User/Result/UserResult.php b/src/Services/User/Result/UserResult.php new file mode 100644 index 00000000..01e0ee4b --- /dev/null +++ b/src/Services/User/Result/UserResult.php @@ -0,0 +1,16 @@ +getCoreResponse()->getResponseData()->getResult()); + } +} \ No newline at end of file diff --git a/src/Services/User/Result/UsersResult.php b/src/Services/User/Result/UsersResult.php new file mode 100644 index 00000000..a4fff780 --- /dev/null +++ b/src/Services/User/Result/UsersResult.php @@ -0,0 +1,27 @@ +getCoreResponse()->getResponseData()->getResult() as $item) { + $res[] = new UserItemResult($item); + } + + return $res; + } +} \ No newline at end of file diff --git a/src/Services/User/Service/User.php b/src/Services/User/Service/User.php new file mode 100644 index 00000000..80228317 --- /dev/null +++ b/src/Services/User/Service/User.php @@ -0,0 +1,120 @@ +core->call('user.fields')); + } + + /** + * Get current user + * @return UserResult + * @throws BaseException + * @throws TransportException + * @link https://training.bitrix24.com/rest_help/users/user_current.php + */ + public function current(): UserResult + { + return new UserResult($this->core->call('user.current')); + } + + /** + * Invites a user. Available only for users with invitation permissions, usually an administrator. Sends a standard account invitation to the user on success. + * + * @param array $fields = ['ID','XML_ID','ACTIVE','NAME','LAST_NAME','SECOND_NAME','TITLE','EMAIL','PERSONAL_PHONE','WORK_PHONE','WORK_POSITION','WORK_COMPANY','IS_ONLINE','TIME_ZONE','TIMESTAMP_X','TIME_ZONE_OFFSET','DATE_REGISTER','LAST_ACTIVITY_DATE','PERSONAL_PROFESSION','PERSONAL_GENDER','PERSONAL_BIRTHDAY','PERSONAL_PHOTO','PERSONAL_FAX','PERSONAL_MOBILE','PERSONAL_PAGER','PERSONAL_STREET','PERSONAL_MAILBOX','PERSONAL_CITY','PERSONAL_STATE','PERSONAL_ZIP','PERSONAL_COUNTRY','PERSONAL_NOTES','WORK_DEPARTMENT','WORK_WWW','WORK_FAX','WORK_PAGER','WORK_STREET','WORK_MAILBOX','WORK_CITY','WORK_STATE','WORK_ZIP','WORK_COUNTRY','WORK_PROFILE','WORK_LOGO','WORK_NOTES','UF_DEPARTMENT','UF_DISTRICT','UF_SKYPE','UF_SKYPE_LINK','UF_ZOOM','UF_TWITTER','UF_FACEBOOK','UF_LINKEDIN','UF_XING','UF_WEB_SITES','UF_PHONE_INNER','UF_EMPLOYMENT_DATE','UF_TIMEMAN','UF_SKILLS','UF_INTERESTS','USER_TYPE'] + * @param string $messageText + * @return AddedItemResult + * @throws BaseException + * @throws TransportException + * @link https://training.bitrix24.com/rest_help/users/user_add.php + */ + public function add(array $fields, string $messageText = ''): AddedItemResult + { + if (!array_key_exists('EXTRANET', $fields)) { + throw new InvalidArgumentException(sprintf('field EXTRANET is required')); + } + + return new AddedItemResult($this->core->call( + 'user.add', + array_merge( + $fields, + [ + 'MESSAGE_TEXT' => $messageText + ] + ) + )); + } + + /** + * @param array $order + * @param array $filter = ['ID','XML_ID','ACTIVE','NAME','LAST_NAME','SECOND_NAME','TITLE','EMAIL','PERSONAL_PHONE','WORK_PHONE','WORK_POSITION','WORK_COMPANY','IS_ONLINE','TIME_ZONE','TIMESTAMP_X','TIME_ZONE_OFFSET','DATE_REGISTER','LAST_ACTIVITY_DATE','PERSONAL_PROFESSION','PERSONAL_GENDER','PERSONAL_BIRTHDAY','PERSONAL_PHOTO','PERSONAL_FAX','PERSONAL_MOBILE','PERSONAL_PAGER','PERSONAL_STREET','PERSONAL_MAILBOX','PERSONAL_CITY','PERSONAL_STATE','PERSONAL_ZIP','PERSONAL_COUNTRY','PERSONAL_NOTES','WORK_DEPARTMENT','WORK_WWW','WORK_FAX','WORK_PAGER','WORK_STREET','WORK_MAILBOX','WORK_CITY','WORK_STATE','WORK_ZIP','WORK_COUNTRY','WORK_PROFILE','WORK_LOGO','WORK_NOTES','UF_DEPARTMENT','UF_DISTRICT','UF_SKYPE','UF_SKYPE_LINK','UF_ZOOM','UF_TWITTER','UF_FACEBOOK','UF_LINKEDIN','UF_XING','UF_WEB_SITES','UF_PHONE_INNER','UF_EMPLOYMENT_DATE','UF_TIMEMAN','UF_SKILLS','UF_INTERESTS','USER_TYPE'] + * @param bool $isAdminMode + * @return UsersResult + * @throws BaseException + * @throws TransportException + */ + public function get(array $order, array $filter, bool $isAdminMode = false): UsersResult + { + return new UsersResult($this->core->call('user.get', [ + 'sort' => array_keys($order)[0], + 'order' => array_values($order)[0], + 'filter' => $filter, + 'ADMIN_MODE' => $isAdminMode ? 'true' : 'false' + ])); + } + + /** + * Updates user information. Available only for users with invitation permissions. + * @param int $userId + * @param array $fields + * @return UpdatedItemResult + * @throws BaseException + * @throws TransportException + * @link https://training.bitrix24.com/rest_help/users/user_update.php + */ + public function update(int $userId, array $fields): UpdatedItemResult + { + return new UpdatedItemResult($this->core->call('user.update', array_merge( + $fields, + [ + 'ID' => $userId + ] + ))); + } + + /** + * This method is used to retrieve list of users with expedited personal data search (name, last name, middle name, name of department, position). Works in two modes: Quick mode, via Fulltext Index and slower mode via right LIKE (support is determined automatically). + * + * @param array $filterFields + * @return UsersResult + * @throws BaseException + * @throws TransportException + * @link https://training.bitrix24.com/rest_help/users/user_search.php + */ + public function search(array $filterFields): UsersResult + { + return new UsersResult($this->core->call('user.search', $filterFields)); + } +} \ No newline at end of file diff --git a/src/Services/User/UserServiceBuilder.php b/src/Services/User/UserServiceBuilder.php new file mode 100644 index 00000000..d6977687 --- /dev/null +++ b/src/Services/User/UserServiceBuilder.php @@ -0,0 +1,25 @@ +serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new User($this->core, $this->log); + } + + return $this->serviceCache[__METHOD__]; + } +} \ No newline at end of file diff --git a/tests/Integration/Services/User/Service/UserTest.php b/tests/Integration/Services/User/Service/UserTest.php new file mode 100644 index 00000000..e8f0ab26 --- /dev/null +++ b/tests/Integration/Services/User/Service/UserTest.php @@ -0,0 +1,132 @@ +userService->search([ + 'NAME' => 'test', + ]); + $this->assertGreaterThanOrEqual(1, $users->getCoreResponse()->getResponseData()->getPagination()->getTotal()); + } + + /** + * @return void + * @throws BaseException + * @throws TransportException + * @covers \Bitrix24\SDK\Services\User\Service\User::get + * @testdox test get users list with internal phone + */ + public function testGetWithInternalPhone(): void + { + $this->assertGreaterThanOrEqual( + 1, + $this->userService->get(['ID' => 'ASC'], [ + '!UF_PHONE_INNER' => null + ], true)->getCoreResponse()->getResponseData()->getPagination()->getTotal() + ); + } + + /** + * @covers \Bitrix24\SDK\Services\User\Service\User::get + * @testdox test get users list + * @return void + * @throws BaseException + * @throws TransportException + */ + public function testGet(): void + { + $this->assertGreaterThanOrEqual( + 1, + $this->userService->get(['ID' => 'ASC'], [], true)->getCoreResponse()->getResponseData()->getPagination()->getTotal() + ); + } + + public function testUpdate(): void + { + $newUser = [ + 'NAME' => 'Test', + 'EMAIL' => sprintf('%s.test@test.com', time()), + 'EXTRANET' => 'N', + 'UF_DEPARTMENT' => [1] + ]; + $userId = $this->userService->add($newUser)->getId(); + $this->assertTrue($this->userService->update($userId, ['NAME' => 'Test2'])->isSuccess()); + $this->assertEquals( + 'Test2', + $this->userService->get(['ID' => 'ASC'], [ + 'ID' => $userId + ])->getUsers()[0]->NAME + ); + } + + /** + * @covers \Bitrix24\SDK\Services\User\Service\User::add + * @testdox test add user + * @return void + * @throws BaseException + * @throws TransportException + */ + public function testAdd(): void + { + $newUser = [ + 'NAME' => 'Test', + 'EMAIL' => sprintf('%s.test@test.com', time()), + 'EXTRANET' => 'N', + 'UF_DEPARTMENT' => [1] + ]; + $userId = $this->userService->add($newUser)->getId(); + $this->assertGreaterThanOrEqual(1, $userId); + } + + /** + * @covers \Bitrix24\SDK\Services\User\Service\User::current + * @testdox test get current user + * @return void + * @throws BaseException + * @throws TransportException + */ + public function testUserCurrent(): void + { + $this->assertInstanceOf(UserItemResult::class, $this->userService->current()->user()); + } + + /** + * @covers \Bitrix24\SDK\Services\User\Service\User::fields + * @testdox test get user fields + * @return void + * @throws BaseException + * @throws TransportException + */ + public function testGetUserFields(): void + { + $this->assertIsArray($this->userService->fields()->getFieldsDescription()); + } + + public function setUp(): void + { + $this->userService = Fabric::getServiceBuilder()->getUserScope()->user(); + } +} \ No newline at end of file From e0bd1baf2c6c83717f7a5e0196ef172669592430 Mon Sep 17 00:00:00 2001 From: mesilov Date: Fri, 17 Mar 2023 15:10:21 +0400 Subject: [PATCH 50/94] move cli commands Signed-off-by: mesilov --- CHANGELOG.md | 1 + {tools/bin => bin}/console | 15 ++--- .../GenerateContactsCommand.php | 34 +++++----- .../PerformanceBenchmarks/ListCommand.php | 18 +++--- .../ShowFieldsDescriptionCommand.php | 62 +++++++++---------- 5 files changed, 62 insertions(+), 68 deletions(-) rename {tools/bin => bin}/console (73%) rename tools/{DemoDataGenerators/CRM/Contacts => Commands}/GenerateContactsCommand.php (90%) rename tools/{ => Commands}/PerformanceBenchmarks/ListCommand.php (98%) rename tools/{ => Commands}/ShowFieldsDescriptionCommand.php (88%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94600c47..34531975 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ * fix errors in `ShowFieldsDescriptionCommand` metadata reader CLI command ### etc +* move CLI entry point to `bin/console` ## 2.0-alpha.7 — 8.08.2022 diff --git a/tools/bin/console b/bin/console similarity index 73% rename from tools/bin/console rename to bin/console index c0c6a43f..86ccde03 100644 --- a/tools/bin/console +++ b/bin/console @@ -1,11 +1,12 @@ #!/usr/bin/env php hasParameterOption('--no-debug', true)) { putenv('APP_DEBUG=' . $_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); } -(new Dotenv())->bootEnv(dirname(__DIR__) . '/.env'); +(new Dotenv())->bootEnv(dirname(__DIR__) . '/tools/.env'); if ($_SERVER['APP_DEBUG']) { umask(0000); @@ -44,9 +45,9 @@ if ($_SERVER['APP_DEBUG']) { } } -$log = new Logger('demo-data-generator'); +$log = new Logger('bitrix24-php-sdk-cli'); $log->pushHandler(new StreamHandler($_ENV['LOGS_FILE'], (int)$_ENV['LOGS_LEVEL'])); -$log->pushProcessor(new \Monolog\Processor\MemoryUsageProcessor(true, true)); +$log->pushProcessor(new MemoryUsageProcessor(true, true)); $application = new Application(); $application->add(new GenerateContactsCommand($log)); diff --git a/tools/DemoDataGenerators/CRM/Contacts/GenerateContactsCommand.php b/tools/Commands/GenerateContactsCommand.php similarity index 90% rename from tools/DemoDataGenerators/CRM/Contacts/GenerateContactsCommand.php rename to tools/Commands/GenerateContactsCommand.php index baaa96b4..26880314 100644 --- a/tools/DemoDataGenerators/CRM/Contacts/GenerateContactsCommand.php +++ b/tools/Commands/GenerateContactsCommand.php @@ -2,8 +2,10 @@ declare(strict_types=1); -namespace Bitrix24\SDK\Tools\DemoDataGenerators\CRM\Contacts; +namespace Bitrix24\SDK\Tools\Commands; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; use Bitrix24\SDK\Core\Batch; use Bitrix24\SDK\Core\BulkItemsReader\BulkItemsReaderBuilder; use Bitrix24\SDK\Core\CoreBuilder; @@ -13,27 +15,23 @@ use Bitrix24\SDK\Services\ServiceBuilder; use InvalidArgumentException; use Psr\Log\LoggerInterface; -use Symfony\Component\Console\Command\Command; + use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -/** - * Class GenerateContactsCommand - * - * @package Bitrix24\SDK\Tools\DemoDataGenerators\CRM\Contacts - */ +#[AsCommand( + name: 'b24:generate:contacts', + description: 'generate demo-data contacts in CRM', + hidden: false +)] class GenerateContactsCommand extends Command { /** * @var LoggerInterface */ protected LoggerInterface $logger; - /** - * @var string - */ - protected static $defaultName = 'generate:contacts'; protected const CONTACTS_COUNT = 'count'; protected const WEBHOOK_URL = 'webhook'; @@ -76,7 +74,7 @@ protected function configure(): void } /** - * @param \Symfony\Component\Console\Input\InputInterface $input + * @param \Symfony\Component\Console\Input\InputInterface $input * @param \Symfony\Component\Console\Output\OutputInterface $output * * @return void @@ -90,7 +88,7 @@ protected function interact(InputInterface $input, OutputInterface $output): voi } /** - * @param InputInterface $input + * @param InputInterface $input * @param OutputInterface $output * * @return int @@ -159,7 +157,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int ); } $timeEnd = microtime(true); - $io->writeln(sprintf('batch query duration: %s seconds', round($timeEnd - $timeStart, 2)) . PHP_EOL . PHP_EOL); + $io->writeln(GenerateContactsCommand . phpsprintf('batch query duration: %s seconds', round($timeEnd - $timeStart, 2)) . PHP_EOL . PHP_EOL); $io->success('contacts added'); } catch (BaseException $exception) { $io = new SymfonyStyle($input, $output); @@ -194,13 +192,13 @@ protected function generateContacts(int $contactsCount): array $contacts = []; for ($i = 0; $i < $contactsCount; $i++) { $contacts[] = [ - 'NAME' => sprintf('name_%s', $i), - 'LAST_NAME' => sprintf('last_%s', $i), + 'NAME' => sprintf('name_%s', $i), + 'LAST_NAME' => sprintf('last_%s', $i), 'SECOND_NAME' => sprintf('second_%s', $i), - 'PHONE' => [ + 'PHONE' => [ ['VALUE' => sprintf('+7978%s', random_int(1000000, 9999999)), 'VALUE_TYPE' => 'MOBILE'], ], - 'EMAIL' => [ + 'EMAIL' => [ ['VALUE' => sprintf('test-%s@gmail.com', random_int(1000000, 9999999)), 'VALUE_TYPE' => 'WORK'], ], ]; diff --git a/tools/PerformanceBenchmarks/ListCommand.php b/tools/Commands/PerformanceBenchmarks/ListCommand.php similarity index 98% rename from tools/PerformanceBenchmarks/ListCommand.php rename to tools/Commands/PerformanceBenchmarks/ListCommand.php index a06c78a9..88ed9fc0 100644 --- a/tools/PerformanceBenchmarks/ListCommand.php +++ b/tools/Commands/PerformanceBenchmarks/ListCommand.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Bitrix24\SDK\Tools\PerformanceBenchmarks; +namespace Bitrix24\SDK\Tools\Commands\PerformanceBenchmarks; use Bitrix24\SDK\Core\Batch; use Bitrix24\SDK\Core\Contracts\BatchOperationsInterface; @@ -24,21 +24,19 @@ use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Component\Console\Attribute\AsCommand; -/** - * Class ListCommand - * - * @package Bitrix24\SDK\Tools\PerformanceBenchmarks - */ + +#[AsCommand( + name: 'b24:benchmark:list', + description: 'performance benchmark for *.list method', + hidden: false +)] class ListCommand extends Command { protected LoggerInterface $logger; protected CoreInterface $core; protected BatchOperationsInterface $batch; - /** - * @var string - */ - protected static $defaultName = 'benchmark:list'; protected const TIME_PRECISION = 4; protected const SELECT_FIELDS_MODE = 'fields'; protected const ELEMENTS_COUNT = 'count'; diff --git a/tools/ShowFieldsDescriptionCommand.php b/tools/Commands/ShowFieldsDescriptionCommand.php similarity index 88% rename from tools/ShowFieldsDescriptionCommand.php rename to tools/Commands/ShowFieldsDescriptionCommand.php index af6254c9..c24d8733 100644 --- a/tools/ShowFieldsDescriptionCommand.php +++ b/tools/Commands/ShowFieldsDescriptionCommand.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Bitrix24\SDK\Tools; +namespace Bitrix24\SDK\Tools\Commands; use Bitrix24\SDK\Core\Contracts\CoreInterface; use Bitrix24\SDK\Core\CoreBuilder; @@ -18,20 +18,18 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Console\Attribute\AsCommand; -/** - * Class ShowFieldsDescriptionCommand - * - * @package Bitrix24\SDK\Tools\PerformanceBenchmarks - */ + +#[AsCommand( + name: 'b24:util:show-fields-description', + description: 'show entity fields description with table or phpDoc output format', + hidden: false +)] class ShowFieldsDescriptionCommand extends Command { protected LoggerInterface $logger; protected CoreInterface $core; - /** - * @var string - */ - protected static $defaultName = 'util:show-fields-description'; protected const WEBHOOK_URL = 'webhook'; protected const OUTPUT_FORMAT = 'output-format'; @@ -71,7 +69,7 @@ protected function configure(): void } /** - * @param InputInterface $input + * @param InputInterface $input * @param OutputInterface $output * * @return int @@ -160,7 +158,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int /** * @param OutputInterface $output - * @param Response $fields + * @param Response $fields * * @throws BaseException */ @@ -175,7 +173,7 @@ private function showFieldsAsPhpDocFunctionSelectSuggest(OutputInterface $output /** * @param OutputInterface $output - * @param Response $fields + * @param Response $fields * * @throws BaseException */ @@ -183,16 +181,15 @@ private function showFieldsAsPhpDocFunctionProperty(OutputInterface $output, Res { $fieldsList = ['*', '* @param array{']; foreach ($fields->getResponseData()->getResult() as $fieldCode => $fieldDescription) { - switch (strtolower($fieldDescription['type'])) { - case 'integer': - $phpDocType = 'int'; - break; - case 'datetime': - $phpDocType = 'string'; - break; - default: - $phpDocType = 'string'; + if (is_array($fieldDescription)) { + $phpDocType = match (strtolower($fieldDescription['type'])) { + 'integer' => 'int', + default => 'string', + }; + } else { + $phpDocType = 'mixed'; } + $fieldsList[] = sprintf('* %s?: %s,', $fieldCode, $phpDocType); } $fieldsList[] = '* } $fields'; @@ -202,7 +199,7 @@ private function showFieldsAsPhpDocFunctionProperty(OutputInterface $output, Res /** * @param OutputInterface $output - * @param Response $fields + * @param Response $fields * * @throws BaseException */ @@ -210,15 +207,14 @@ private function showFieldsAsPhpDocClassHeader(OutputInterface $output, Response { $fieldsList = ['/**', '*']; foreach ($fields->getResponseData()->getResult() as $fieldCode => $fieldDescription) { - switch (strtolower($fieldDescription['type'])) { - case 'integer': - $phpDocType = 'int'; - break; - case 'datetime': - $phpDocType = 'string'; - break; - default: - $phpDocType = 'string'; + + if (is_array($fieldDescription)) { + $phpDocType = match (strtolower($fieldDescription['type'])) { + 'integer' => 'int', + default => 'string', + }; + } else { + $phpDocType = 'mixed'; } $fieldsList[] = sprintf('* @property-read %s $%s', $phpDocType, $fieldCode); } @@ -228,7 +224,7 @@ private function showFieldsAsPhpDocClassHeader(OutputInterface $output, Response /** * @param OutputInterface $output - * @param Response $fields + * @param Response $fields * * @throws BaseException */ From 6f31b6b50a91d896c65c3f17b1d84b546b2f050a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Sun, 19 Mar 2023 11:32:35 +0300 Subject: [PATCH 51/94] =?UTF-8?q?-=20=D0=A1=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD?= =?UTF-8?q?=D0=B0=201=20=D1=87=D0=B0=D1=81=D1=82=D1=8C=20=D0=B3=D0=B5?= =?UTF-8?q?=D0=BD=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BA=D0=BE=D0=BD?= =?UTF-8?q?=D1=82=D0=B0=D0=BA=D1=82=D0=BE=D0=B2=20=D1=81=20=D0=B4=D0=B2?= =?UTF-8?q?=D1=83=D0=BC=D1=8F=20=D0=B2=D0=B8=D0=B4=D0=B0=D0=BC=D0=B8=20?= =?UTF-8?q?=D1=82=D0=B5=D0=BB=D0=B5=D1=84=D0=BE=D0=BD=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OperatingTimingTest.php | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 tests/Integration/OperatingTimingTest/OperatingTimingTest.php diff --git a/tests/Integration/OperatingTimingTest/OperatingTimingTest.php b/tests/Integration/OperatingTimingTest/OperatingTimingTest.php new file mode 100644 index 00000000..60cf7dbe --- /dev/null +++ b/tests/Integration/OperatingTimingTest/OperatingTimingTest.php @@ -0,0 +1,70 @@ + sprintf('first_%s', time()), + 'SECOND' => sprintf('second_%s', time()), + 'PHONE' => [ + [ + 'VALUE' => $phoneNumberWork, + 'VALUE_TYPE' => 'WORK' + ], + [ + 'VALUE' => $phoneNumberHome, + 'VALUE_TYPE' => 'HOME' + ] + ] + ]; + + } + foreach ($masContacts as $contact){ + var_dump($contact); + $contactId[] = $this->contactService->add($contact)->getId(); + foreach ($contactId as $id){ + $contactUpdate= $this->contactService->update($id,['PHONE'[0] => '']); + } + + self::assertGreaterThanOrEqual(1,$contactId); + } + var_dump($contactId); + + + } + + public function setUp(): void + { + $this->contactService = Fabric::getServiceBuilder()->getCRMScope()->contact(); + } +} \ No newline at end of file From 9009ce586b1ecde48d8e6d71c4d74079bce6a012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Wed, 22 Mar 2023 16:55:23 +0300 Subject: [PATCH 52/94] =?UTF-8?q?-=20=D0=90=D0=BF=D0=B4=D0=B5=D0=B9=D1=82?= =?UTF-8?q?=20=D0=B1=D0=B5=D0=B7=20=D0=B1=D0=B0=D1=82=D1=87=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OperatingTimingTest.php | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/tests/Integration/OperatingTimingTest/OperatingTimingTest.php b/tests/Integration/OperatingTimingTest/OperatingTimingTest.php index 60cf7dbe..2a676b40 100644 --- a/tests/Integration/OperatingTimingTest/OperatingTimingTest.php +++ b/tests/Integration/OperatingTimingTest/OperatingTimingTest.php @@ -4,6 +4,7 @@ namespace Bitrix24\SDK\Tests\Integration\OperatingTimingTest; +use Bitrix24\SDK\Core\Batch; use Bitrix24\SDK\Core\Exceptions\BaseException; use Bitrix24\SDK\Core\Exceptions\TransportException; use Bitrix24\SDK\Services\CRM\Contact\Service\Contact; @@ -19,52 +20,51 @@ class OperatingTimingTest extends TestCase { protected Contact $contactService; + private Batch $batch; /** * @throws \Bitrix24\SDK\Core\Exceptions\TransportException * @throws \Bitrix24\SDK\Core\Exceptions\BaseException */ - public function testOperatingTiming() + public function testOperatingTiming(): void { - $PhoneNumWorkMas = []; - $PhoneNumHomeMas = []; $masContacts = []; - for($i=0; $i<5; $i++){ + for ($i = 0; $i < 5; $i++) { $phoneNumberWork = sprintf('+7%s', time()); $phoneNumberHome = sprintf('%s', microtime()); - $phoneNumberHome = implode("-",str_split(substr($phoneNumberHome,2,-13),2)); + $phoneNumberHome = implode("-", str_split(substr($phoneNumberHome, 2, -13), 2)); $masContacts[] = [ - 'NAME' => sprintf('first_%s', time()), - 'SECOND' => sprintf('second_%s', time()), - 'PHONE' => [ - [ - 'VALUE' => $phoneNumberWork, - 'VALUE_TYPE' => 'WORK' + 'fields' => [ + 'NAME' => sprintf('first_%s', time()), + 'SECOND' => sprintf('second_%s', time()), + 'PHONE' => [ + [ + 'VALUE' => $phoneNumberWork, + 'VALUE_TYPE' => 'WORK' + ], + [ + 'VALUE' => $phoneNumberHome, + 'VALUE_TYPE' => 'HOME' + ] ], - [ - 'VALUE' => $phoneNumberHome, - 'VALUE_TYPE' => 'HOME' - ] ] ]; } - foreach ($masContacts as $contact){ - var_dump($contact); - $contactId[] = $this->contactService->add($contact)->getId(); - foreach ($contactId as $id){ - $contactUpdate= $this->contactService->update($id,['PHONE'[0] => '']); - } - - self::assertGreaterThanOrEqual(1,$contactId); + $contactsIdList = []; + foreach ($this->batch->addEntityItems('crm.contact.add', $masContacts) as $addContactResult) { + $contactsIdList[] = $addContactResult->getResult(); } - var_dump($contactId); - - + foreach ($contactsIdList as $contactId) { + $contactPhoneId = $this->contactService->get($contactId[0])->contact()->PHONE[0]['ID']; + $contactUpdate = $this->contactService->update((int)$contactId[0],['PHONE' => [['ID' => $contactPhoneId, 'VALUE' => ""]]],[ "REGISTER_SONET_EVENT" => "Y"]); + } + $this->assertCount(5, $contactsIdList); } public function setUp(): void { + $this->batch = Fabric::getBatchService(); $this->contactService = Fabric::getServiceBuilder()->getCRMScope()->contact(); } } \ No newline at end of file From 0d4b2fdcd9ae4caff2344ddb5c5cf7d121cd17d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Wed, 22 Mar 2023 17:51:00 +0300 Subject: [PATCH 53/94] =?UTF-8?q?-=20=D0=A2=D0=B5=D1=81=D1=82=20=D0=BD?= =?UTF-8?q?=D0=B0=2010=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Integration/OperatingTimingTest/OperatingTimingTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/OperatingTimingTest/OperatingTimingTest.php b/tests/Integration/OperatingTimingTest/OperatingTimingTest.php index 2a676b40..1e35c665 100644 --- a/tests/Integration/OperatingTimingTest/OperatingTimingTest.php +++ b/tests/Integration/OperatingTimingTest/OperatingTimingTest.php @@ -29,7 +29,7 @@ class OperatingTimingTest extends TestCase public function testOperatingTiming(): void { $masContacts = []; - for ($i = 0; $i < 5; $i++) { + for ($i = 0; $i < 10000; $i++) { $phoneNumberWork = sprintf('+7%s', time()); $phoneNumberHome = sprintf('%s', microtime()); $phoneNumberHome = implode("-", str_split(substr($phoneNumberHome, 2, -13), 2)); From f3699426d2c75aebd80a766cf508da2e411dab5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Thu, 23 Mar 2023 17:43:13 +0300 Subject: [PATCH 54/94] =?UTF-8?q?-=20=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=20=D1=82=D0=B5=D1=81=D1=82=20=D0=B4=D0=BB=D1=8F=20=D0=B3?= =?UTF-8?q?=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D1=85=20!!!=20-=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=87=D0=B0=D1=81=D1=82=D1=8C?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=B4=D0=B0=20=D0=B2=20=D1=82=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=B5=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D0=BE=D0=B8=D1=81=D0=BA?= =?UTF-8?q?=D0=B0=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=BD=D1=82=D0=B0=D0=BA=D1=82=D0=BE=D0=B2=20?= =?UTF-8?q?=D1=81=20=D0=BF=D0=BE=D0=BC=D0=BE=D1=89=D1=8C=D1=8E=20=D0=B1?= =?UTF-8?q?=D0=B0=D1=82=D1=87=D0=B0=20=D0=B8=20=D0=BC=D0=B5=D1=82=D0=BE?= =?UTF-8?q?=D0=B4=D0=B0=20list.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OperatingTimingTest.php | 26 ++++++++++++------- .../CRM/Contacts/GenerateContactsCommand.php | 5 ++-- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/tests/Integration/OperatingTimingTest/OperatingTimingTest.php b/tests/Integration/OperatingTimingTest/OperatingTimingTest.php index 1e35c665..88c1dc06 100644 --- a/tests/Integration/OperatingTimingTest/OperatingTimingTest.php +++ b/tests/Integration/OperatingTimingTest/OperatingTimingTest.php @@ -5,10 +5,8 @@ namespace Bitrix24\SDK\Tests\Integration\OperatingTimingTest; use Bitrix24\SDK\Core\Batch; -use Bitrix24\SDK\Core\Exceptions\BaseException; -use Bitrix24\SDK\Core\Exceptions\TransportException; + use Bitrix24\SDK\Services\CRM\Contact\Service\Contact; -use Bitrix24\SDK\Services\ServiceBuilder; use Bitrix24\SDK\Tests\Integration\Fabric; use PHPUnit\Framework\TestCase; @@ -28,7 +26,7 @@ class OperatingTimingTest extends TestCase */ public function testOperatingTiming(): void { - $masContacts = []; + /* $masContacts = []; for ($i = 0; $i < 10000; $i++) { $phoneNumberWork = sprintf('+7%s', time()); $phoneNumberHome = sprintf('%s', microtime()); @@ -50,16 +48,24 @@ public function testOperatingTiming(): void ] ]; - } - $contactsIdList = []; + }*/ + /* $contactsIdList = []; foreach ($this->batch->addEntityItems('crm.contact.add', $masContacts) as $addContactResult) { $contactsIdList[] = $addContactResult->getResult(); + }*/ + $cnt = 0; + foreach ($this->contactService->batch->list([], ['>ID' => '103575'], ['ID','PHONE'], 5) as $contactList) { + $cnt++; + $contactListId[] = $contactList->ID; + $contactListPhone[] = $contactList->PHONE; + } - foreach ($contactsIdList as $contactId) { - $contactPhoneId = $this->contactService->get($contactId[0])->contact()->PHONE[0]['ID']; - $contactUpdate = $this->contactService->update((int)$contactId[0],['PHONE' => [['ID' => $contactPhoneId, 'VALUE' => ""]]],[ "REGISTER_SONET_EVENT" => "Y"]); + foreach ($contactListId as $contactId) { + $contactPhoneId = $this->contactService->get($contactId)->contact()->PHONE[0]['ID']; + $contactUpdate = $this->contactService->update((int)$contactId,['PHONE' => [['ID' => $contactPhoneId, 'VALUE' => ""]]],[ "REGISTER_SONET_EVENT" => "Y"]); } - $this->assertCount(5, $contactsIdList); + self::assertGreaterThanOrEqual(5, $contactListId); + } public function setUp(): void diff --git a/tools/DemoDataGenerators/CRM/Contacts/GenerateContactsCommand.php b/tools/DemoDataGenerators/CRM/Contacts/GenerateContactsCommand.php index baaa96b4..c983863f 100644 --- a/tools/DemoDataGenerators/CRM/Contacts/GenerateContactsCommand.php +++ b/tools/DemoDataGenerators/CRM/Contacts/GenerateContactsCommand.php @@ -199,10 +199,11 @@ protected function generateContacts(int $contactsCount): array 'SECOND_NAME' => sprintf('second_%s', $i), 'PHONE' => [ ['VALUE' => sprintf('+7978%s', random_int(1000000, 9999999)), 'VALUE_TYPE' => 'MOBILE'], + ['VALUE' => implode("-", str_split(substr( sprintf('%s', microtime()), 2, -13), 2))], ], - 'EMAIL' => [ + /* 'EMAIL' => [ ['VALUE' => sprintf('test-%s@gmail.com', random_int(1000000, 9999999)), 'VALUE_TYPE' => 'WORK'], - ], + ],*/ ]; } From fe616ece0c5eb4282ef1d816d30a6f7128d9d28f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Thu, 23 Mar 2023 17:43:13 +0300 Subject: [PATCH 55/94] =?UTF-8?q?-=20=D0=93=D0=B5=D0=BD=D0=B5=D1=80=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D1=8F=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=B4=D0=BA=D1=80=D1=83=D1=87=D0=B5=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D0=BD=D0=B0=D1=88=D0=B5=D0=B3=D0=BE=20?= =?UTF-8?q?=D1=82=D0=B5=D1=81=D1=82=D0=B0!=20-=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=87=D0=B0=D1=81=D1=82=D1=8C?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=B4=D0=B0=20=D0=B2=20=D1=82=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=B5=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D0=BE=D0=B8=D1=81=D0=BA?= =?UTF-8?q?=D0=B0=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=BD=D1=82=D0=B0=D0=BA=D1=82=D0=BE=D0=B2=20?= =?UTF-8?q?=D1=81=20=D0=BF=D0=BE=D0=BC=D0=BE=D1=89=D1=8C=D1=8E=20=D0=B1?= =?UTF-8?q?=D0=B0=D1=82=D1=87=D0=B0=20=D0=B8=20=D0=BC=D0=B5=D1=82=D0=BE?= =?UTF-8?q?=D0=B4=D0=B0=20list.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OperatingTimingTest.php | 26 ++++++++++++------- .../CRM/Contacts/GenerateContactsCommand.php | 5 ++-- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/tests/Integration/OperatingTimingTest/OperatingTimingTest.php b/tests/Integration/OperatingTimingTest/OperatingTimingTest.php index 1e35c665..88c1dc06 100644 --- a/tests/Integration/OperatingTimingTest/OperatingTimingTest.php +++ b/tests/Integration/OperatingTimingTest/OperatingTimingTest.php @@ -5,10 +5,8 @@ namespace Bitrix24\SDK\Tests\Integration\OperatingTimingTest; use Bitrix24\SDK\Core\Batch; -use Bitrix24\SDK\Core\Exceptions\BaseException; -use Bitrix24\SDK\Core\Exceptions\TransportException; + use Bitrix24\SDK\Services\CRM\Contact\Service\Contact; -use Bitrix24\SDK\Services\ServiceBuilder; use Bitrix24\SDK\Tests\Integration\Fabric; use PHPUnit\Framework\TestCase; @@ -28,7 +26,7 @@ class OperatingTimingTest extends TestCase */ public function testOperatingTiming(): void { - $masContacts = []; + /* $masContacts = []; for ($i = 0; $i < 10000; $i++) { $phoneNumberWork = sprintf('+7%s', time()); $phoneNumberHome = sprintf('%s', microtime()); @@ -50,16 +48,24 @@ public function testOperatingTiming(): void ] ]; - } - $contactsIdList = []; + }*/ + /* $contactsIdList = []; foreach ($this->batch->addEntityItems('crm.contact.add', $masContacts) as $addContactResult) { $contactsIdList[] = $addContactResult->getResult(); + }*/ + $cnt = 0; + foreach ($this->contactService->batch->list([], ['>ID' => '103575'], ['ID','PHONE'], 5) as $contactList) { + $cnt++; + $contactListId[] = $contactList->ID; + $contactListPhone[] = $contactList->PHONE; + } - foreach ($contactsIdList as $contactId) { - $contactPhoneId = $this->contactService->get($contactId[0])->contact()->PHONE[0]['ID']; - $contactUpdate = $this->contactService->update((int)$contactId[0],['PHONE' => [['ID' => $contactPhoneId, 'VALUE' => ""]]],[ "REGISTER_SONET_EVENT" => "Y"]); + foreach ($contactListId as $contactId) { + $contactPhoneId = $this->contactService->get($contactId)->contact()->PHONE[0]['ID']; + $contactUpdate = $this->contactService->update((int)$contactId,['PHONE' => [['ID' => $contactPhoneId, 'VALUE' => ""]]],[ "REGISTER_SONET_EVENT" => "Y"]); } - $this->assertCount(5, $contactsIdList); + self::assertGreaterThanOrEqual(5, $contactListId); + } public function setUp(): void diff --git a/tools/DemoDataGenerators/CRM/Contacts/GenerateContactsCommand.php b/tools/DemoDataGenerators/CRM/Contacts/GenerateContactsCommand.php index baaa96b4..c983863f 100644 --- a/tools/DemoDataGenerators/CRM/Contacts/GenerateContactsCommand.php +++ b/tools/DemoDataGenerators/CRM/Contacts/GenerateContactsCommand.php @@ -199,10 +199,11 @@ protected function generateContacts(int $contactsCount): array 'SECOND_NAME' => sprintf('second_%s', $i), 'PHONE' => [ ['VALUE' => sprintf('+7978%s', random_int(1000000, 9999999)), 'VALUE_TYPE' => 'MOBILE'], + ['VALUE' => implode("-", str_split(substr( sprintf('%s', microtime()), 2, -13), 2))], ], - 'EMAIL' => [ + /* 'EMAIL' => [ ['VALUE' => sprintf('test-%s@gmail.com', random_int(1000000, 9999999)), 'VALUE_TYPE' => 'WORK'], - ], + ],*/ ]; } From eacb965c50871e74e478439f8039a138042f8121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Thu, 23 Mar 2023 20:04:35 +0300 Subject: [PATCH 56/94] =?UTF-8?q?-=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=20update=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20batch=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0?= =?UTF-8?q?=D1=8E=D1=89=D0=B5=D0=B3=D0=BE=20=D1=81=20=D0=BA=D0=BE=D0=BD?= =?UTF-8?q?=D1=82=D0=B0=D0=BA=D1=82=D0=B0=D0=BC=D0=B8.=20-=20=D0=92=20?= =?UTF-8?q?=D1=82=D0=B5=D1=81=D1=82=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=87=D0=B0=D1=81=D1=82=D1=8C=20=D1=81=20?= =?UTF-8?q?update=20=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D1=83=D1=8E?= =?UTF-8?q?=D1=89=D0=B0=D1=8F=20=D0=B1=D0=B0=D1=82=D1=87.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Services/CRM/Contact/Service/Batch.php | 22 +++++++++ .../OperatingTimingTest.php | 47 +++++-------------- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/src/Services/CRM/Contact/Service/Batch.php b/src/Services/CRM/Contact/Service/Batch.php index 696720a2..02cfec3f 100644 --- a/src/Services/CRM/Contact/Service/Batch.php +++ b/src/Services/CRM/Contact/Service/Batch.php @@ -7,6 +7,7 @@ use Bitrix24\SDK\Core\Exceptions\BaseException; use Bitrix24\SDK\Core\Result\AddedItemBatchResult; use Bitrix24\SDK\Core\Result\DeletedItemBatchResult; +use Bitrix24\SDK\Core\Result\UpdatedItemBatchResult; use Bitrix24\SDK\Services\AbstractBatchService; use Bitrix24\SDK\Services\CRM\Contact\Result\ContactItemResult; use Generator; @@ -224,4 +225,25 @@ public function delete(array $contactId): Generator yield $key => new DeletedItemBatchResult($item); } } + + /** + * Update contact + * + * Update elements in array with structure + * element_id => [ // deal id + * 'fields' => [], // deal fields to update + * 'params' => [] + * ] + * + * @param array $entityItems + * + * @return \Generator + * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + */ + public function update(array $entityItems): Generator + { + foreach ($this->batch->updateEntityItems('crm.contact.update', $entityItems) as $key => $item) { + yield $key => new UpdatedItemBatchResult($item); + } + } } \ No newline at end of file diff --git a/tests/Integration/OperatingTimingTest/OperatingTimingTest.php b/tests/Integration/OperatingTimingTest/OperatingTimingTest.php index 88c1dc06..fb928de6 100644 --- a/tests/Integration/OperatingTimingTest/OperatingTimingTest.php +++ b/tests/Integration/OperatingTimingTest/OperatingTimingTest.php @@ -3,9 +3,7 @@ declare(strict_types=1); namespace Bitrix24\SDK\Tests\Integration\OperatingTimingTest; - use Bitrix24\SDK\Core\Batch; - use Bitrix24\SDK\Services\CRM\Contact\Service\Contact; use Bitrix24\SDK\Tests\Integration\Fabric; use PHPUnit\Framework\TestCase; @@ -18,7 +16,7 @@ class OperatingTimingTest extends TestCase { protected Contact $contactService; - private Batch $batch; + protected Batch $batch; /** * @throws \Bitrix24\SDK\Core\Exceptions\TransportException @@ -26,44 +24,23 @@ class OperatingTimingTest extends TestCase */ public function testOperatingTiming(): void { - /* $masContacts = []; - for ($i = 0; $i < 10000; $i++) { - $phoneNumberWork = sprintf('+7%s', time()); - $phoneNumberHome = sprintf('%s', microtime()); - $phoneNumberHome = implode("-", str_split(substr($phoneNumberHome, 2, -13), 2)); - $masContacts[] = [ - 'fields' => [ - 'NAME' => sprintf('first_%s', time()), - 'SECOND' => sprintf('second_%s', time()), - 'PHONE' => [ - [ - 'VALUE' => $phoneNumberWork, - 'VALUE_TYPE' => 'WORK' - ], - [ - 'VALUE' => $phoneNumberHome, - 'VALUE_TYPE' => 'HOME' - ] - ], - ] - ]; - }*/ - /* $contactsIdList = []; - foreach ($this->batch->addEntityItems('crm.contact.add', $masContacts) as $addContactResult) { - $contactsIdList[] = $addContactResult->getResult(); - }*/ $cnt = 0; - foreach ($this->contactService->batch->list([], ['>ID' => '103575'], ['ID','PHONE'], 5) as $contactList) { + $contactsToUpdate = []; + foreach ($this->contactService->batch->list([], ['>ID' => '103595'], ['ID','PHONE'], 30000) as $contactList) { $cnt++; + $contactsToUpdate[$contactList->ID] = [ + 'fields' => [ + 'PHONE' => [['ID' =>$contactList->PHONE[0]['ID']]] + ], + 'params' => [], + ]; $contactListId[] = $contactList->ID; - $contactListPhone[] = $contactList->PHONE; - } - foreach ($contactListId as $contactId) { - $contactPhoneId = $this->contactService->get($contactId)->contact()->PHONE[0]['ID']; - $contactUpdate = $this->contactService->update((int)$contactId,['PHONE' => [['ID' => $contactPhoneId, 'VALUE' => ""]]],[ "REGISTER_SONET_EVENT" => "Y"]); + foreach ($this->contactService->batch->update($contactsToUpdate) as $dealUpdateResult) { + $this->assertTrue($dealUpdateResult->isSuccess()); } + self::assertGreaterThanOrEqual(5, $contactListId); } From 61b2d364238172947aa5b687af2003a395f5112a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Sat, 25 Mar 2023 01:00:25 +0300 Subject: [PATCH 57/94] =?UTF-8?q?-=20=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BE=D0=BF=D0=B5=D1=87=D0=B0=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=B2=20=D0=BE=D0=BF=D0=B8=D1=81=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B8=20=D0=BA=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D1=83.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Services/CRM/Contact/Service/Batch.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Services/CRM/Contact/Service/Batch.php b/src/Services/CRM/Contact/Service/Batch.php index 02cfec3f..e75165d3 100644 --- a/src/Services/CRM/Contact/Service/Batch.php +++ b/src/Services/CRM/Contact/Service/Batch.php @@ -230,8 +230,8 @@ public function delete(array $contactId): Generator * Update contact * * Update elements in array with structure - * element_id => [ // deal id - * 'fields' => [], // deal fields to update + * element_id => [ // contact id + * 'fields' => [], // contact fields to update * 'params' => [] * ] * From e3ffe75cf59202b14cd974d057faf4da10b00a9c Mon Sep 17 00:00:00 2001 From: mesilov Date: Tue, 28 Mar 2023 01:15:56 +0400 Subject: [PATCH 58/94] add batch update for contact Signed-off-by: mesilov --- CHANGELOG.md | 97 +++++++---- src/Core/Batch.php | 162 +++++++++--------- src/Services/CRM/Contact/Service/Batch.php | 23 +++ .../CRM/Contact/Service/ContactBatchTest.php | 122 +++++++++++++ .../CRM/Contact/Service/ContactTest.php | 29 ---- 5 files changed, 292 insertions(+), 141 deletions(-) create mode 100644 tests/Integration/Services/CRM/Contact/Service/ContactBatchTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 34531975..16659f2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,23 +1,26 @@ # bitrix24-php-sdk change log -## 2.0-beta.1 — 25.02.2023 +## 2.0-beta.1 — 25.03.2023 ### Added * add `Symfony\Component\Uid\Uuid` requirements -* add contracts for bitrix24 applications based on bitrix24-php-sdk - `Bitrix24\SDK\Application\Contracts`, now added `Bitrix24Account` +* add contracts for bitrix24 applications based on bitrix24-php-sdk - `Bitrix24\SDK\Application\Contracts`, now + added `Bitrix24Account` * add [service builder factory](https://github.com/mesilov/bitrix24-php-sdk/issues/328) * add method `Bitrix24\SDK\Core\Credentials\Scope::initFromString` * add method `Bitrix24\SDK\Application\ApplicationStatus::initFromString` * ❗️add php 8.2 support * add system CRM multi-field type `Bitrix24\SDK\Services\CRM\Common\Result\SystemFields\Types\Phone` -* add scope `user`,`user_basic`,`user_brief`,`user.userfield` and services [add scope user support](https://github.com/mesilov/bitrix24-php-sdk/issues/339) - * `Bitrix24\SDK\Services\User\Service\User::fields` - get user fields - * `Bitrix24\SDK\Services\User\Service\User::current` - get current user - * `Bitrix24\SDK\Services\User\Service\User::add` - add user - * `Bitrix24\SDK\Services\User\Service\User::get` - get user - * `Bitrix24\SDK\Services\User\Service\User::update` - update user - * `Bitrix24\SDK\Services\User\Service\User::search` - search users +* add scope `user`,`user_basic`,`user_brief`,`user.userfield` and + services [add scope user support](https://github.com/mesilov/bitrix24-php-sdk/issues/339) + * `Bitrix24\SDK\Services\User\Service\User::fields` - get user fields + * `Bitrix24\SDK\Services\User\Service\User::current` - get current user + * `Bitrix24\SDK\Services\User\Service\User::add` - add user + * `Bitrix24\SDK\Services\User\Service\User::get` - get user + * `Bitrix24\SDK\Services\User\Service\User::update` - update user + * `Bitrix24\SDK\Services\User\Service\User::search` - search users +* add method `\Bitrix24\SDK\Services\CRM\Contact\Service\Batch::update()` for batch update contacts ### Changed @@ -43,6 +46,7 @@ * fix errors in `ShowFieldsDescriptionCommand` metadata reader CLI command ### etc + * move CLI entry point to `bin/console` ## 2.0-alpha.7 — 8.08.2022 @@ -50,34 +54,46 @@ ### Added * add new scope `Telephony` and services [add Telephony support](https://github.com/mesilov/bitrix24-php-sdk/issues/291) -* add new scope `UserConsent` and services [add UserConsent support](https://github.com/mesilov/bitrix24-php-sdk/issues/285) -* add new scope `Placements` and services [add Placements support](https://github.com/mesilov/bitrix24-php-sdk/issues/274) -* add new scope `IMOpenLines` and services [add IM Open Lines support](https://github.com/mesilov/bitrix24-php-sdk/issues/302) -* add in scope `CRM` new service `Leads` in scope «CRM» [add Leads support](https://github.com/mesilov/bitrix24-php-sdk/issues/282) -* add in scope `CRM` new service `Activity` in scope «CRM» [add Activity support](https://github.com/mesilov/bitrix24-php-sdk/issues/283) +* add new scope `UserConsent` and + services [add UserConsent support](https://github.com/mesilov/bitrix24-php-sdk/issues/285) +* add new scope `Placements` and + services [add Placements support](https://github.com/mesilov/bitrix24-php-sdk/issues/274) +* add new scope `IMOpenLines` and + services [add IM Open Lines support](https://github.com/mesilov/bitrix24-php-sdk/issues/302) +* add in scope `CRM` new service `Leads` in scope + «CRM» [add Leads support](https://github.com/mesilov/bitrix24-php-sdk/issues/282) +* add in scope `CRM` new service `Activity` in scope + «CRM» [add Activity support](https://github.com/mesilov/bitrix24-php-sdk/issues/283) * add in scope `CRM` for entity Deal method `Services\CRM\Deal\Service\Batch::update` batch update deals * add in scope `CRM` for entity Contact method `Services\CRM\Contact\Service\Batch::delete` batch delete contacts -* add in scope `CRM` [read models](https://github.com/mesilov/bitrix24-php-sdk/issues/300) for activity `Services\CRM\Activity\ReadModel` +* add in scope `CRM` [read models](https://github.com/mesilov/bitrix24-php-sdk/issues/300) for + activity `Services\CRM\Activity\ReadModel` for activity types: `EmailFetcher`, `OpenLineFetcher`, `VoximplantFetcher`, `WebFormFetcher` -* add in scope «Main» new service `Events` [add incoming events support](https://github.com/mesilov/bitrix24-php-sdk/issues/296) +* add in scope «Main» new + service `Events` [add incoming events support](https://github.com/mesilov/bitrix24-php-sdk/issues/296) * add support Application level events: `ONAPPINSTALL` and `ONAPPUNINSTALL` [add incoming events support](https://github.com/mesilov/bitrix24-php-sdk/issues/296) * add support Application level event: `PortalDomainUrlChangedEvent` -* add method `Core\Batch::updateEntityItems` for [update items in batch mode](https://github.com/mesilov/bitrix24-php-sdk/issues/268) and +* add method `Core\Batch::updateEntityItems` + for [update items in batch mode](https://github.com/mesilov/bitrix24-php-sdk/issues/268) and integration test * add method to interface `Core\Contracts\BatchInterface::updateEntityItems` for update items in batch mode * add in scope `Placements` service `Placement\Service\UserFieldType` for work with user fields embedding -* add in scope `Telephony` add events: `OnExternalCallBackStart`, `OnExternalCallStart`, `OnVoximplantCallEnd`, `OnVoximplantCallEnd` - , `OnVoximplantCallInit`, `OnVoximplantCallStart` see [add telephony events](https://github.com/mesilov/bitrix24-php-sdk/issues/304) +* add in scope `Telephony` add + events: `OnExternalCallBackStart`, `OnExternalCallStart`, `OnVoximplantCallEnd`, `OnVoximplantCallEnd` + , `OnVoximplantCallInit`, `OnVoximplantCallStart` + see [add telephony events](https://github.com/mesilov/bitrix24-php-sdk/issues/304) * add `ApplicationStatus` with application status codes description * add fabric method `AccessToken::initFromPlacementRequest` when application init form placement request * add fabric method `ApplicationProfile::initFromArray` when application profile stored in ENV-variables * add `Bitrix24\SDK\Application\Requests\Placement\PlacementRequest` for application data from placements * add fabric method `Credentials::initFromPlacementRequest` when application init form placement request * add method `Services\Main\Service::getServerTime` returns current server time in the format YYYY-MM-DDThh:mm:ss±hh:mm. -* add method `Services\Main\Service::getCurrentUserProfile` return basic Information about the current user without any scopes +* add method `Services\Main\Service::getCurrentUserProfile` return basic Information about the current user without any + scopes * add method `Services\Main\Service::getAccessName` returns access permission names. -* add method `Services\Main\Service::checkUserAccess` Checks if the current user has at least one permission of those specified by the +* add method `Services\Main\Service::checkUserAccess` Checks if the current user has at least one permission of those + specified by the ACCESS parameter. * add method `Services\Main\Service::getMethodAffordability` Method returns 2 parameters - isExisting and isAvailable * add money type support by [phpmoney](https://github.com/moneyphp/money) @@ -85,16 +101,20 @@ ### Changed -* update scope list [расширить и актуализировать доступные скоупы](https://github.com/mesilov/bitrix24-php-sdk/issues/280) +* update scope + list [расширить и актуализировать доступные скоупы](https://github.com/mesilov/bitrix24-php-sdk/issues/280) * bump `symfony/*` to `6.*` version requirement. * method `Services\Main\Service::getAvailableMethods` marks as deprecated * method `Services\Main\Service::getAllMethods` marks as deprecated * method `Services\Main\Service::getMethodsByScope` marks as deprecated * ❗️fabric methods `Bitrix24\SDK\Core\Credentials` - renamed and now are [consistent](https://github.com/mesilov/bitrix24-php-sdk/issues/303): `createFromWebhook`, `createFromOAuth` + renamed and now + are [consistent](https://github.com/mesilov/bitrix24-php-sdk/issues/303): `createFromWebhook`, `createFromOAuth` , `createFromPlacementRequest` -* ❗️deleted [unused class](https://github.com/mesilov/bitrix24-php-sdk/issues/303) `Bitrix24\SDK\Core\Response\DTO\ResponseDataCollection` -* ❗️deleted [redundant class](https://github.com/mesilov/bitrix24-php-sdk/issues/303) `Bitrix24\SDK\Core\Response\DTO\Result` +* +❗️deleted [unused class](https://github.com/mesilov/bitrix24-php-sdk/issues/303) `Bitrix24\SDK\Core\Response\DTO\ResponseDataCollection` +* +❗️deleted [redundant class](https://github.com/mesilov/bitrix24-php-sdk/issues/303) `Bitrix24\SDK\Core\Response\DTO\Result` * ❗️deleted [method](https://github.com/mesilov/bitrix24-php-sdk/issues/303) `CoreBuilder::withWebhookUrl`, use method `CoreBuilder::withCredentials` @@ -102,7 +122,8 @@ * add bugfix for batch method for reverse order queries * fix type compatible errors for `Core\Result\AbstractItem` -* fix error in `NetworkTimingParser`, [error in NetworkTimingsErrorInfo](https://github.com/mesilov/bitrix24-php-sdk/issues/277) +* fix error + in `NetworkTimingParser`, [error in NetworkTimingsErrorInfo](https://github.com/mesilov/bitrix24-php-sdk/issues/277) * fix error in `RenewedAccessToken` DTO, remove `Scope` enum [UnknownScopeCodeException - in refresh token response](https://github.com/mesilov/bitrix24-php-sdk/issues/295) @@ -116,18 +137,22 @@ * add «fast» batch-query without counting elements in result recordset [Добавить поддержку выгрузки большого количества данных без подсчёта элементов -1](https://github.com/mesilov/bitrix24-php-sdk/issues/248) -* add `Credentials` in CoreBuilder [set credentials from core builder](https://github.com/mesilov/bitrix24-php-sdk/pull/246) +* add `Credentials` in + CoreBuilder [set credentials from core builder](https://github.com/mesilov/bitrix24-php-sdk/pull/246) * add method `Core\Batch::deleteEntityItems` for delete items in batch mode and integration test * add integration test for read strategy `FilterWithBatchWithoutCountOrderTest` * add type check in method `Core\Batch::deleteEntityItems` - only integer id allowed * add interface `Core\Contracts\DeletedItemResultInterface` * add in scope «CRM» `Services\CRM\Deal\Service\Batch::delete` batch delete deals * add `symfony/stopwatch` component for integration tests -* add `/Infrastructure/HttpClient/TransportLayer/NetworkTimingsParser` for parse `curl_info` network data structures for debug logs +* add `/Infrastructure/HttpClient/TransportLayer/NetworkTimingsParser` for parse `curl_info` network data structures for + debug logs in `Bitrix24\SDK\Core\Response::__destruct()` -* add `/Infrastructure/HttpClient/TransportLayer/ResponseInfoParser` for parse `bitrix24_rest_api` timing info for debug logs +* add `/Infrastructure/HttpClient/TransportLayer/ResponseInfoParser` for parse `bitrix24_rest_api` timing info for debug + logs in `Bitrix24\SDK\Core\Response::__destruct()` -* add `Bitrix24\SDK\Core\BulkItemsReader` for data-intensive applications for bulk export data from Bitrix24, read strategies located in +* add `Bitrix24\SDK\Core\BulkItemsReader` for data-intensive applications for bulk export data from Bitrix24, read + strategies located in folder `ReadStrategies`, in services read model **must** use most effective read strategy. * add integration tests in GitHub Actions pipeline 🎉, now integration tests run on push on `dev-branch` * add incoming webhook for run integration tests `vendor-check.yml` from vendor CI\CD pipeline @@ -138,7 +163,8 @@ * switch `symfony/http-client-contracts` to `^2.5` version requirement. * switch `symfony/event-dispatcher` to `5.4.*` version requirement. * switch `ramsey/uuid` to `^4.2.3` version requirement. -* switch `psr/log` to `^1.1.4 || ^2.0 || ^3.0` [version requirement](https://github.com/mesilov/bitrix24-php-sdk/issues/245). +* switch `psr/log` + to `^1.1.4 || ^2.0 || ^3.0` [version requirement](https://github.com/mesilov/bitrix24-php-sdk/issues/245). ## 2.0-alpha.5 – 28.11.2021 @@ -161,7 +187,8 @@ ### Changed -* update type definition for `ContactItemResult`, now return types will be cast to real types: DateTimeInterface, int, boolean etc +* update type definition for `ContactItemResult`, now return types will be cast to real types: DateTimeInterface, int, + boolean etc ## 2.0-alpha.4 – 25.11.2021 @@ -283,10 +310,12 @@ branch version 1.x – bugfix and security releases only ## 0.5.0 (4.09.2016) -* add class `Bitrix24\CRM\Quote` see pr [Added support for Quote API calls](https://github.com/mesilov/bitrix24-php-sdk/pull/53/) +* add class `Bitrix24\CRM\Quote` see + pr [Added support for Quote API calls](https://github.com/mesilov/bitrix24-php-sdk/pull/53/) * add support http status 301 moved permanently in class `Bitrix24` see issue [301 Moved Permanently #49](https://github.com/mesilov/bitrix24-php-sdk/issues/49) -* fixed bug in class `Bitrix24` see pr [Issue in the isAccessTokenExpire method](https://github.com/mesilov/bitrix24-php-sdk/pull/54) +* fixed bug in class `Bitrix24` see + pr [Issue in the isAccessTokenExpire method](https://github.com/mesilov/bitrix24-php-sdk/pull/54) ## 0.4.1 (4.08.2016) diff --git a/src/Core/Batch.php b/src/Core/Batch.php index d4123b87..c28bd28f 100644 --- a/src/Core/Batch.php +++ b/src/Core/Batch.php @@ -34,7 +34,7 @@ class Batch implements BatchOperationsInterface /** * Batch constructor. * - * @param CoreInterface $core + * @param CoreInterface $core * @param LoggerInterface $log */ public function __construct(CoreInterface $core, LoggerInterface $log) @@ -62,7 +62,7 @@ protected function clearCommands(): void /** * Add entity items with batch call * - * @param string $apiMethod + * @param string $apiMethod * @param array $entityItems * * @return Generator|ResponseData[] @@ -73,7 +73,7 @@ public function addEntityItems(string $apiMethod, array $entityItems): Generator $this->logger->debug( 'addEntityItems.start', [ - 'apiMethod' => $apiMethod, + 'apiMethod' => $apiMethod, 'entityItems' => $entityItems, ] ); @@ -105,7 +105,7 @@ public function addEntityItems(string $apiMethod, array $entityItems): Generator /** * Delete entity items with batch call * - * @param string $apiMethod + * @param string $apiMethod * @param array $entityItemId * * @return Generator|ResponseData[] @@ -116,7 +116,7 @@ public function deleteEntityItems(string $apiMethod, array $entityItemId): Gener $this->logger->debug( 'deleteEntityItems.start', [ - 'apiMethod' => $apiMethod, + 'apiMethod' => $apiMethod, 'entityItems' => $entityItemId, ] ); @@ -173,7 +173,7 @@ public function deleteEntityItems(string $apiMethod, array $entityItemId): Gener * 'params' => [] // optional fields * ] * - * @param string $apiMethod + * @param string $apiMethod * @param array> $entityItems * * @return Generator|ResponseData[] @@ -184,7 +184,7 @@ public function updateEntityItems(string $apiMethod, array $entityItems): Genera $this->logger->debug( 'updateEntityItems.start', [ - 'apiMethod' => $apiMethod, + 'apiMethod' => $apiMethod, 'entityItems' => $entityItems, ] ); @@ -207,11 +207,14 @@ public function updateEntityItems(string $apiMethod, array $entityItems): Genera ); } - $this->registerCommand($apiMethod, [ - 'id' => $entityItemId, - 'fields' => $entityItem['fields'], - 'params' => $entityItem['params'], - ]); + $cmdArguments = [ + 'id' => $entityItemId, + 'fields' => $entityItem['fields'] + ]; + if (array_key_exists('params', $entityItem)) { + $cmdArguments['params'] = $entityItem['params']; + } + $this->registerCommand($apiMethod, $cmdArguments); } foreach ($this->getTraversable(true) as $cnt => $updatedItemResult) { @@ -244,24 +247,25 @@ public function updateEntityItems(string $apiMethod, array $entityItems): Genera /** * Register api command to command collection for batch calls * - * @param string $apiMethod + * @param string $apiMethod * @param array $parameters - * @param string|null $commandName - * @param callable|null $callback not implemented + * @param string|null $commandName + * @param callable|null $callback not implemented * * @throws \Exception */ protected function registerCommand( - string $apiMethod, - array $parameters = [], - ?string $commandName = null, + string $apiMethod, + array $parameters = [], + ?string $commandName = null, callable $callback = null - ): void { + ): void + { $this->logger->debug( 'registerCommand.start', [ - 'apiMethod' => $apiMethod, - 'parameters' => $parameters, + 'apiMethod' => $apiMethod, + 'parameters' => $parameters, 'commandName' => $commandName, ] ); @@ -317,11 +321,11 @@ protected function getReverseOrder(array $order): array /** * Get traversable list without count elements * - * @param string $apiMethod + * @param string $apiMethod * @param array $order - * @param array $filter - * @param array $select - * @param int|null $limit + * @param array $filter + * @param array $select + * @param int|null $limit * * @return \Generator * @throws \Bitrix24\SDK\Core\Exceptions\BaseException @@ -333,19 +337,20 @@ protected function getReverseOrder(array $order): array */ public function getTraversableList( string $apiMethod, - array $order, - array $filter, - array $select, - ?int $limit = null - ): Generator { + array $order, + array $filter, + array $select, + ?int $limit = null + ): Generator + { $this->logger->debug( 'getTraversableList.start', [ 'apiMethod' => $apiMethod, - 'order' => $order, - 'filter' => $filter, - 'select' => $select, - 'limit' => $limit, + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'limit' => $limit, ] ); @@ -379,10 +384,10 @@ public function getTraversableList( $firstResultPage = $this->core->call( $apiMethod, [ - 'order' => $order, + 'order' => $order, 'filter' => $filter, 'select' => $select, - 'start' => 0, + 'start' => 0, ] ); $totalElementsCount = $firstResultPage->getResponseData()->getPagination()->getTotal(); @@ -425,10 +430,10 @@ public function getTraversableList( $lastResultPage = $this->core->call( $apiMethod, [ - 'order' => $this->getReverseOrder($order), + 'order' => $this->getReverseOrder($order), 'filter' => $filter, 'select' => $select, - 'start' => 0, + 'start' => 0, ] ); $lastElementId = (int)$lastResultPage->getResponseData()->getResult()[0]['ID']; @@ -440,7 +445,7 @@ public function getTraversableList( } $this->logger->debug('getTraversableList.lastElementsId', [ 'lastElementIdInFirstPage' => $lastElementIdInFirstPage, - 'lastElementId' => $lastElementId, + 'lastElementId' => $lastElementId, ]); // register commands with updated filter @@ -448,9 +453,9 @@ public function getTraversableList( $lastElementIdInFirstPage++; for ($startId = $lastElementIdInFirstPage; $startId <= $lastElementId; $startId += self::MAX_ELEMENTS_IN_PAGE) { $this->logger->debug('registerCommand.item', [ - 'startId' => $startId, + 'startId' => $startId, 'lastElementId' => $lastElementId, - 'delta' => $lastElementId - $startId, + 'delta' => $lastElementId - $startId, ]); $delta = $lastElementId - $startId; @@ -468,10 +473,10 @@ public function getTraversableList( $this->registerCommand( $apiMethod, [ - 'order' => [], + 'order' => [], 'filter' => $this->updateFilterForBatch($startId, $lastElementIdInPage, $isLastPage, $filter), 'select' => $select, - 'start' => -1, + 'start' => -1, ] ); } @@ -492,8 +497,8 @@ public function getTraversableList( 'getTraversableList.batchResultItem', [ 'batchCommandItemNumber' => $queryCnt, - 'nextItem' => $queryResultData->getPagination()->getNextItem(), - 'durationTime' => $queryResultData->getTime()->getDuration(), + 'nextItem' => $queryResultData->getPagination()->getNextItem(), + 'durationTime' => $queryResultData->getTime()->getDuration(), ] ); // iterate items in batch query result @@ -509,9 +514,9 @@ public function getTraversableList( } /** - * @param int $startElementId - * @param int $lastElementId - * @param bool $isLastPage + * @param int $startElementId + * @param int $lastElementId + * @param bool $isLastPage * @param array $oldFilter * * @return array @@ -520,15 +525,15 @@ protected function updateFilterForBatch(int $startElementId, int $lastElementId, { $this->logger->debug('updateFilterForBatch.start', [ 'startElementId' => $startElementId, - 'lastElementId' => $lastElementId, - 'isLastPage' => $isLastPage, - 'oldFilter' => $oldFilter, + 'lastElementId' => $lastElementId, + 'isLastPage' => $isLastPage, + 'oldFilter' => $oldFilter, ]); $filter = array_merge( $oldFilter, [ - '>=ID' => $startElementId, + '>=ID' => $startElementId, $isLastPage ? '<=ID' : ' $lastElementId, ] ); @@ -544,11 +549,11 @@ protected function updateFilterForBatch(int $startElementId, int $lastElementId, * * work with start item position and elements count * - * @param string $apiMethod + * @param string $apiMethod * @param array $order - * @param array $filter - * @param array $select - * @param int|null $limit + * @param array $filter + * @param array $select + * @param int|null $limit * * @return Generator * @throws BaseException @@ -561,19 +566,20 @@ protected function updateFilterForBatch(int $startElementId, int $lastElementId, */ public function getTraversableListWithCount( string $apiMethod, - array $order, - array $filter, - array $select, - ?int $limit = null - ): Generator { + array $order, + array $filter, + array $select, + ?int $limit = null + ): Generator + { $this->logger->debug( 'getTraversableListWithCount.start', [ 'apiMethod' => $apiMethod, - 'order' => $order, - 'filter' => $filter, - 'select' => $select, - 'limit' => $limit, + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'limit' => $limit, ] ); $this->clearCommands(); @@ -582,10 +588,10 @@ public function getTraversableListWithCount( $firstResult = $this->core->call( $apiMethod, [ - 'order' => $order, + 'order' => $order, 'filter' => $filter, 'select' => $select, - 'start' => 0, + 'start' => 0, ] ); @@ -595,7 +601,7 @@ public function getTraversableListWithCount( $this->logger->debug( 'getTraversableListWithCount.calculateCommandsRange', [ - 'nextItem' => $nextItem, + 'nextItem' => $nextItem, 'totalItems' => $total, ] ); @@ -606,10 +612,10 @@ public function getTraversableListWithCount( $this->registerCommand( $apiMethod, [ - 'order' => $order, + 'order' => $order, 'filter' => $filter, 'select' => $select, - 'start' => $startItem, + 'start' => $startItem, ] ); if ($limit !== null && $limit < $startItem) { @@ -621,10 +627,10 @@ public function getTraversableListWithCount( $this->registerCommand( $apiMethod, [ - 'order' => $order, + 'order' => $order, 'filter' => $filter, 'select' => $select, - 'start' => 0, + 'start' => 0, ] ); } @@ -632,7 +638,7 @@ public function getTraversableListWithCount( $this->logger->debug( 'getTraversableListWithCount.commandsRegistered', [ - 'commandsCount' => $this->commands->count(), + 'commandsCount' => $this->commands->count(), 'totalItemsToSelect' => $total, ] ); @@ -647,8 +653,8 @@ public function getTraversableListWithCount( 'getTraversableListWithCount.batchResultItem', [ 'batchCommandItemNumber' => $queryCnt, - 'nextItem' => $queryResultData->getPagination()->getNextItem(), - 'durationTime' => $queryResultData->getTime()->getDuration(), + 'nextItem' => $queryResultData->getPagination()->getNextItem(), + 'durationTime' => $queryResultData->getTime()->getDuration(), ] ); // iterate items in batch query result @@ -692,8 +698,8 @@ protected function getTraversable(bool $isHaltOnError): Generator $this->logger->debug( 'getTraversable.batchResultItem.processStart', [ - 'batchItemNumber' => $batchItem, - 'batchApiCommand' => $batchResult->getApiCommand()->getApiMethod(), + 'batchItemNumber' => $batchItem, + 'batchApiCommand' => $batchResult->getApiCommand()->getApiMethod(), 'batchApiCommandUuid' => $batchResult->getApiCommand()->getUuid()->toString(), ] ); @@ -765,7 +771,7 @@ private function getTraversableBatchResults(bool $isHaltOnError): Generator 'getTraversableBatchResults.batchQuery', [ 'batchQueryNumber' => $batchQueryCounter, - 'queriesCount' => count($batchQuery), + 'queriesCount' => count($batchQuery), ] ); // batch call diff --git a/src/Services/CRM/Contact/Service/Batch.php b/src/Services/CRM/Contact/Service/Batch.php index 696720a2..01383afe 100644 --- a/src/Services/CRM/Contact/Service/Batch.php +++ b/src/Services/CRM/Contact/Service/Batch.php @@ -7,6 +7,7 @@ use Bitrix24\SDK\Core\Exceptions\BaseException; use Bitrix24\SDK\Core\Result\AddedItemBatchResult; use Bitrix24\SDK\Core\Result\DeletedItemBatchResult; +use Bitrix24\SDK\Core\Result\UpdatedItemBatchResult; use Bitrix24\SDK\Services\AbstractBatchService; use Bitrix24\SDK\Services\CRM\Contact\Result\ContactItemResult; use Generator; @@ -210,6 +211,28 @@ public function add(array $contacts): Generator } } + /** + * Batch update contacts + * + * Update elements in array with structure + * element_id => [ // contact id + * 'fields' => [], // contact fields to update + * 'params' => [] + * ] + * + * @param array $entityItems + * + * @param array $entityItems + * @return Generator + * @throws BaseException + */ + public function update(array $entityItems): Generator + { + foreach ($this->batch->updateEntityItems('crm.contact.update', $entityItems) as $key => $item) { + yield $key => new UpdatedItemBatchResult($item); + } + } + /** * Batch delete contact items * diff --git a/tests/Integration/Services/CRM/Contact/Service/ContactBatchTest.php b/tests/Integration/Services/CRM/Contact/Service/ContactBatchTest.php new file mode 100644 index 00000000..fafd8cb5 --- /dev/null +++ b/tests/Integration/Services/CRM/Contact/Service/ContactBatchTest.php @@ -0,0 +1,122 @@ +contactService->add(['NAME' => 'test contact']); + $cnt = 0; + + foreach ($this->contactService->batch->list([], ['>ID' => '1'], ['ID', 'NAME'], 1) as $item) { + $cnt++; + } + self::assertGreaterThanOrEqual(1, $cnt); + } + + /** + * @throws BaseException + * @throws TransportException + * @covers \Bitrix24\SDK\Services\CRM\Contact\Service\Batch::add() + */ + public function testBatchAdd(): void + { + $contacts = []; + for ($i = 1; $i < 60; $i++) { + $contacts[] = ['NAME' => 'name-' . $i]; + } + $cnt = 0; + foreach ($this->contactService->batch->add($contacts) as $item) { + $cnt++; + } + + self::assertEquals(count($contacts), $cnt); + } + + /** + * @return void + * @throws BaseException + * @covers \Bitrix24\SDK\Services\CRM\Contact\Service\Batch::update() + */ + public function testBatchUpdate(): void + { + // add contacts + $contacts = []; + for ($i = 1; $i <= self::TEST_SEGMENT_ELEMENTS_COUNT; $i++) { + $contacts[] = [ + 'NAME' => 'name-' . time(), + 'SECOND_NAME' => 'second_name-' . time(), + 'LAST_NAME' => 'last_name-' . time(), + 'PHONE' => [ + [ + 'VALUE' => (new PhoneNumberBuilder())->build(), + 'VALUE_TYPE' => 'WORK' + ] + ] + ]; + } + $cnt = 0; + $contactId = []; + foreach ($this->contactService->batch->add($contacts) as $item) { + $cnt++; + $contactId[] = $item->getId(); + } + self::assertEquals(count($contacts), $cnt); + + // generate update data + $updateContactData = []; + foreach ($contactId as $id) { + $updateContactData[$id] = [ + 'fields' => [ + 'NAME' => 'name-' . $id . '-updated' + ], + ]; + } + + // update contacts in batch mode + $cnt = 0; + foreach ($this->contactService->batch->update($updateContactData) as $item) { + $cnt++; + $this->assertTrue($item->isSuccess()); + } + self::assertEquals(count($contacts), $cnt); + + // delete contacts + $cnt = 0; + foreach ($this->contactService->batch->delete($contactId) as $item) { + $cnt++; + $this->assertTrue($item->isSuccess()); + } + self::assertEquals(count($contacts), $cnt); + } + + public function setUp(): void + { + $this->contactService = Fabric::getServiceBuilder()->getCRMScope()->contact(); + } +} diff --git a/tests/Integration/Services/CRM/Contact/Service/ContactTest.php b/tests/Integration/Services/CRM/Contact/Service/ContactTest.php index 1be0b5e4..58d24b0f 100644 --- a/tests/Integration/Services/CRM/Contact/Service/ContactTest.php +++ b/tests/Integration/Services/CRM/Contact/Service/ContactTest.php @@ -87,35 +87,6 @@ public function testUpdate(): void self::assertEquals($newName, $this->contactService->get($contact->getId())->contact()->NAME); } - /** - * @throws BaseException - * @throws TransportException - */ - public function testBatchList(): void - { - $this->contactService->add(['NAME' => 'test contact']); - $cnt = 0; - - foreach ($this->contactService->batch->list([], ['>ID' => '1'], ['ID', 'NAME'], 1) as $item) { - $cnt++; - } - self::assertGreaterThanOrEqual(1, $cnt); - } - - public function testBatchAdd(): void - { - $contacts = []; - for ($i = 1; $i < 60; $i++) { - $contacts[] = ['NAME' => 'name-' . $i]; - } - $cnt = 0; - foreach ($this->contactService->batch->add($contacts) as $item) { - $cnt++; - } - - self::assertEquals(count($contacts), $cnt); - } - /** * @throws \Bitrix24\SDK\Core\Exceptions\TransportException * @throws \Bitrix24\SDK\Core\Exceptions\BaseException From a966704cf296e1f3b4c808ec50468a9302dba3b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=A5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=BE=D0=B2?= Date: Mon, 3 Apr 2023 17:18:44 +0300 Subject: [PATCH 59/94] =?UTF-8?q?-=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=D0=B8=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=BF=D0=BE=D0=BA=D0=B0=D0=B7=D0=B0=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?=20=D1=81=D1=83=D0=BC=D0=BC=D0=B0=D1=80=D0=BD=D0=BE=D0=B3=D0=BE?= =?UTF-8?q?=20operating=20=D0=B2=D0=BE=20=D0=B2=D1=80=D0=B5=D0=BC=D1=8F=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OperatingTimingTest/OperatingTimingTest.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/Integration/OperatingTimingTest/OperatingTimingTest.php b/tests/Integration/OperatingTimingTest/OperatingTimingTest.php index fb928de6..d83e6ea3 100644 --- a/tests/Integration/OperatingTimingTest/OperatingTimingTest.php +++ b/tests/Integration/OperatingTimingTest/OperatingTimingTest.php @@ -3,6 +3,7 @@ declare(strict_types=1); namespace Bitrix24\SDK\Tests\Integration\OperatingTimingTest; + use Bitrix24\SDK\Core\Batch; use Bitrix24\SDK\Services\CRM\Contact\Service\Contact; use Bitrix24\SDK\Tests\Integration\Fabric; @@ -25,21 +26,26 @@ class OperatingTimingTest extends TestCase public function testOperatingTiming(): void { - $cnt = 0; + $timeStart = microtime(true); $contactsToUpdate = []; - foreach ($this->contactService->batch->list([], ['>ID' => '103595'], ['ID','PHONE'], 30000) as $contactList) { - $cnt++; + foreach ($this->contactService->batch->list([], ['>ID' => '12'], ['ID', 'PHONE'], 30000) as $contactList) { $contactsToUpdate[$contactList->ID] = [ 'fields' => [ - 'PHONE' => [['ID' =>$contactList->PHONE[0]['ID']]] + 'PHONE' => [['ID' => $contactList->PHONE[0]['ID']]] ], 'params' => [], ]; $contactListId[] = $contactList->ID; } foreach ($this->contactService->batch->update($contactsToUpdate) as $dealUpdateResult) { - $this->assertTrue($dealUpdateResult->isSuccess()); + $logOperating[] = $dealUpdateResult->getResponseData()->getTime()->getOperating(); + $logOperatingResetAt = $dealUpdateResult->getResponseData()->getTime()->getOperatingResetAt(); + $sumOperating = array_sum($logOperating); + echo "summa operating: " . $sumOperating . PHP_EOL; + echo "operating rest at: " . $logOperatingResetAt . PHP_EOL; } + $timeEnd = microtime(true); + echo sprintf('batch query duration: %s seconds', round($timeEnd - $timeStart, 2)) . PHP_EOL; self::assertGreaterThanOrEqual(5, $contactListId); From f72a293e8aa3056080eff8e6fc0cf4a5dd7e44ab Mon Sep 17 00:00:00 2001 From: mesilov Date: Sun, 16 Apr 2023 01:41:34 +0400 Subject: [PATCH 60/94] add some code for support operating timings Signed-off-by: mesilov --- src/Core/ApiLevelErrorHandler.php | 70 +++++++- src/Core/Core.php | 39 ++--- .../OperationTimeLimitExceededException.php | 14 ++ src/Core/Response/Response.php | 71 ++------ .../Integration/Core/OperatingTimingTest.php | 153 ++++++++++++++++++ .../OperatingTimingTest.php | 59 ------- tests/Unit/Core/ApiLevelErrorHandlerTest.php | 51 ++++++ tests/Unit/Stubs/NullCore.php | 5 +- 8 files changed, 320 insertions(+), 142 deletions(-) create mode 100644 src/Core/Exceptions/OperationTimeLimitExceededException.php create mode 100644 tests/Integration/Core/OperatingTimingTest.php delete mode 100644 tests/Integration/OperatingTimingTest/OperatingTimingTest.php create mode 100644 tests/Unit/Core/ApiLevelErrorHandlerTest.php diff --git a/src/Core/ApiLevelErrorHandler.php b/src/Core/ApiLevelErrorHandler.php index 685b9331..39b1d5df 100644 --- a/src/Core/ApiLevelErrorHandler.php +++ b/src/Core/ApiLevelErrorHandler.php @@ -6,6 +6,7 @@ use Bitrix24\SDK\Core\Exceptions\BaseException; use Bitrix24\SDK\Core\Exceptions\MethodNotFoundException; +use Bitrix24\SDK\Core\Exceptions\OperationTimeLimitExceededException; use Bitrix24\SDK\Core\Exceptions\QueryLimitExceededException; use Psr\Log\LoggerInterface; @@ -33,33 +34,88 @@ public function __construct(LoggerInterface $logger) } /** - * @param array $responseBody + * @param array $responseBody * * @throws QueryLimitExceededException * @throws BaseException */ public function handle(array $responseBody): void { - if (!array_key_exists(self::ERROR_KEY, $responseBody)) { - $this->logger->debug('handle.noError'); + //ошибка единичного запроса + if (array_key_exists(self::ERROR_KEY, $responseBody) && array_key_exists(self::ERROR_DESCRIPTION_KEY, $responseBody)) { + $this->handleError($responseBody); + } + // ошибка в батче + if (!array_key_exists('result', $responseBody)) { return; } + if (array_key_exists('result_error', $responseBody['result'])) { + foreach ($responseBody['result']['result_error'] as $cmdId => $errorData) { + $this->handleError($errorData, $cmdId); + } + } + } + + /** + * @throws MethodNotFoundException + * @throws QueryLimitExceededException + * @throws BaseException + */ + private function handleError(array $responseBody, ?string $batchCommandId = null): void + { $errorCode = strtolower(trim((string)$responseBody[self::ERROR_KEY])); $errorDescription = strtolower(trim((string)$responseBody[self::ERROR_DESCRIPTION_KEY])); + $this->logger->debug( - 'handle.errorCode', + 'handle.errorInformation', [ 'errorCode' => $errorCode, + 'errorDescription' => $errorDescription, ] ); + + $batchErrorPrefix = ''; + if ($batchCommandId !== null) { + $batchErrorPrefix = sprintf(' batch command id: %s', $batchCommandId); + } + switch ($errorCode) { case 'query_limit_exceeded': - throw new QueryLimitExceededException('query limit exceeded - too many requests'); + throw new QueryLimitExceededException(sprintf('query limit exceeded - too many requests %s', $batchErrorPrefix)); case 'error_method_not_found': - throw new MethodNotFoundException('api method not found'); + throw new MethodNotFoundException(sprintf('api method not found %s %s', $errorDescription, $batchErrorPrefix)); + case 'operation_time_limit': + throw new OperationTimeLimitExceededException(sprintf('operation time limit exceeded %s %s', $errorDescription, $batchErrorPrefix)); default: - throw new BaseException(sprintf('%s - %s', $errorCode, $errorDescription)); + throw new BaseException(sprintf('%s - %s %s', $errorCode, $errorDescription, $batchErrorPrefix)); } + // switch (strtoupper(trim($apiResponse['error']))) { +// case 'EXPIRED_TOKEN': +// throw new Bitrix24TokenIsExpiredException($errorMsg); +// case 'WRONG_CLIENT': +// case 'ERROR_OAUTH': +// $this->log->error($errorMsg, $this->getErrorContext()); +// throw new Bitrix24WrongClientException($errorMsg); +// case 'ERROR_METHOD_NOT_FOUND': +// $this->log->error($errorMsg, $this->getErrorContext()); +// throw new Bitrix24MethodNotFoundException($errorMsg); +// case 'INVALID_TOKEN': +// case 'INVALID_GRANT': +// $this->log->error($errorMsg, $this->getErrorContext()); +// throw new Bitrix24TokenIsInvalidException($errorMsg); + +// case 'PAYMENT_REQUIRED': +// $this->log->error($errorMsg, $this->getErrorContext()); +// throw new Bitrix24PaymentRequiredException($errorMsg); +// case 'NO_AUTH_FOUND': +// $this->log->error($errorMsg, $this->getErrorContext()); +// throw new Bitrix24PortalRenamedException($errorMsg); +// case 'INSUFFICIENT_SCOPE': +// $this->log->error($errorMsg, $this->getErrorContext()); +// throw new Bitrix24InsufficientScope($errorMsg); +// default: +// $this->log->error($errorMsg, $this->getErrorContext()); +// throw new Bitrix24ApiException($errorMsg); } } \ No newline at end of file diff --git a/src/Core/Core.php b/src/Core/Core.php index 688bbe26..a739fe9a 100644 --- a/src/Core/Core.php +++ b/src/Core/Core.php @@ -32,17 +32,18 @@ class Core implements CoreInterface /** * Main constructor. * - * @param ApiClientInterface $apiClient - * @param ApiLevelErrorHandler $apiLevelErrorHandler + * @param ApiClientInterface $apiClient + * @param ApiLevelErrorHandler $apiLevelErrorHandler * @param EventDispatcherInterface $eventDispatcher - * @param LoggerInterface $logger + * @param LoggerInterface $logger */ public function __construct( - ApiClientInterface $apiClient, - ApiLevelErrorHandler $apiLevelErrorHandler, + ApiClientInterface $apiClient, + ApiLevelErrorHandler $apiLevelErrorHandler, EventDispatcherInterface $eventDispatcher, - LoggerInterface $logger - ) { + LoggerInterface $logger + ) + { $this->apiClient = $apiClient; $this->apiLevelErrorHandler = $apiLevelErrorHandler; $this->eventDispatcher = $eventDispatcher; @@ -51,7 +52,7 @@ public function __construct( /** * @param string $apiMethod - * @param array $parameters + * @param array $parameters * * @return Response * @throws BaseException @@ -62,7 +63,7 @@ public function call(string $apiMethod, array $parameters = []): Response $this->logger->debug( 'call.start', [ - 'method' => $apiMethod, + 'method' => $apiMethod, 'parameters' => $parameters, ] ); @@ -80,7 +81,7 @@ public function call(string $apiMethod, array $parameters = []): Response switch ($apiCallResponse->getStatusCode()) { case StatusCodeInterface::STATUS_OK: //todo check with empty response size from server - $response = new Response($apiCallResponse, new Command($apiMethod, $parameters), $this->logger); + $response = new Response($apiCallResponse, new Command($apiMethod, $parameters), $this->apiLevelErrorHandler, $this->logger); break; case StatusCodeInterface::STATUS_FOUND: // change domain url @@ -98,9 +99,9 @@ public function call(string $apiMethod, array $parameters = []): Response $this->logger->debug( 'api call repeated to new domain url', [ - 'domainUrl' => $portalNewDomainUrlHost, + 'domainUrl' => $portalNewDomainUrlHost, 'repeatedApiMethod' => $apiMethod, - 'httpStatusCode' => $response->getHttpResponse()->getStatusCode(), + 'httpStatusCode' => $response->getHttpResponse()->getStatusCode(), ] ); // dispatch event, application listeners update domain url host in accounts repository @@ -121,10 +122,10 @@ public function call(string $apiMethod, array $parameters = []): Response $this->logger->debug( 'access token renewed', [ - 'newAccessToken' => $renewedToken->getAccessToken()->getAccessToken(), + 'newAccessToken' => $renewedToken->getAccessToken()->getAccessToken(), 'newRefreshToken' => $renewedToken->getAccessToken()->getRefreshToken(), - 'newExpires' => $renewedToken->getAccessToken()->getExpires(), - 'appStatus' => $renewedToken->getApplicationStatus(), + 'newExpires' => $renewedToken->getAccessToken()->getExpires(), + 'appStatus' => $renewedToken->getApplicationStatus(), ] ); $this->apiClient->getCredentials()->setAccessToken($renewedToken->getAccessToken()); @@ -135,7 +136,7 @@ public function call(string $apiMethod, array $parameters = []): Response 'api call repeated', [ 'repeatedApiMethod' => $apiMethod, - 'httpStatusCode' => $response->getHttpResponse()->getStatusCode(), + 'httpStatusCode' => $response->getHttpResponse()->getStatusCode(), ] ); @@ -161,7 +162,7 @@ public function call(string $apiMethod, array $parameters = []): Response 'unhandled server status', [ 'httpStatus' => $apiCallResponse->getStatusCode(), - 'body' => $body, + 'body' => $body, ] ); $this->apiLevelErrorHandler->handle($body); @@ -172,7 +173,7 @@ public function call(string $apiMethod, array $parameters = []): Response $this->logger->error( 'call.transportException', [ - 'trace' => $exception->getTrace(), + 'trace' => $exception->getTrace(), 'message' => $exception->getMessage(), ] ); @@ -185,7 +186,7 @@ public function call(string $apiMethod, array $parameters = []): Response 'call.unknownException', [ 'message' => $exception->getMessage(), - 'trace' => $exception->getTrace(), + 'trace' => $exception->getTrace(), ] ); throw new BaseException(sprintf('unknown error - %s', $exception->getMessage()), $exception->getCode(), $exception); diff --git a/src/Core/Exceptions/OperationTimeLimitExceededException.php b/src/Core/Exceptions/OperationTimeLimitExceededException.php new file mode 100644 index 00000000..6311730e --- /dev/null +++ b/src/Core/Exceptions/OperationTimeLimitExceededException.php @@ -0,0 +1,14 @@ +httpResponse = $httpResponse; $this->apiCommand = $apiCommand; + $this->apiLevelErrorHandler = $apiLevelErrorHandler; $this->logger = $logger; $this->responseData = null; } @@ -64,16 +70,16 @@ public function __destruct() $restTimings = null; if ($this->responseData !== null) { $restTimings = [ - 'rest_query_duration' => $this->responseData->getTime()->getDuration(), + 'rest_query_duration' => $this->responseData->getTime()->getDuration(), 'rest_query_processing' => $this->responseData->getTime()->getProcessing(), - 'rest_query_start' => $this->responseData->getTime()->getStart(), - 'rest_query_finish' => $this->responseData->getTime()->getFinish(), + 'rest_query_start' => $this->responseData->getTime()->getStart(), + 'rest_query_finish' => $this->responseData->getTime()->getFinish(), ]; } $this->logger->info('Response.TransportInfo', [ - 'restTimings' => $restTimings, + 'restTimings' => $restTimings, 'networkTimings' => (new NetworkTimingsParser($this->httpResponse->getInfo()))->toArrayWithMicroseconds(), - 'responseInfo' => (new ResponseInfoParser($this->httpResponse->getInfo()))->toArray(), + 'responseInfo' => (new ResponseInfoParser($this->httpResponse->getInfo()))->toArray(), ]); } @@ -92,8 +98,9 @@ public function getResponseData(): DTO\ResponseData $this->logger->info('getResponseData.responseBody', [ 'responseBody' => $responseResult, ]); + // try to handle api-level errors - $this->handleApiLevelErrors($responseResult); + $this->apiLevelErrorHandler->handle($responseResult); if (!is_array($responseResult['result'])) { $responseResult['result'] = [$responseResult['result']]; @@ -143,50 +150,4 @@ private function getHttpResponseContent(): ?string return $content; } - - /** - * @param array $apiResponse - */ - private function handleApiLevelErrors(array $apiResponse): void - { - $this->logger->debug('handleApiLevelErrors.start'); - - if (array_key_exists('error', $apiResponse)) { - $errorMsg = sprintf( - '%s - %s ', - $apiResponse['error'], - (array_key_exists('error_description', $apiResponse) ? $apiResponse['error_description'] : ''), - ); -// todo check api-level error codes -// -// switch (strtoupper(trim($apiResponse['error']))) { -// case 'EXPIRED_TOKEN': -// throw new Bitrix24TokenIsExpiredException($errorMsg); -// case 'WRONG_CLIENT': -// case 'ERROR_OAUTH': -// $this->log->error($errorMsg, $this->getErrorContext()); -// throw new Bitrix24WrongClientException($errorMsg); -// case 'ERROR_METHOD_NOT_FOUND': -// $this->log->error($errorMsg, $this->getErrorContext()); -// throw new Bitrix24MethodNotFoundException($errorMsg); -// case 'INVALID_TOKEN': -// case 'INVALID_GRANT': -// $this->log->error($errorMsg, $this->getErrorContext()); -// throw new Bitrix24TokenIsInvalidException($errorMsg); - -// case 'PAYMENT_REQUIRED': -// $this->log->error($errorMsg, $this->getErrorContext()); -// throw new Bitrix24PaymentRequiredException($errorMsg); -// case 'NO_AUTH_FOUND': -// $this->log->error($errorMsg, $this->getErrorContext()); -// throw new Bitrix24PortalRenamedException($errorMsg); -// case 'INSUFFICIENT_SCOPE': -// $this->log->error($errorMsg, $this->getErrorContext()); -// throw new Bitrix24InsufficientScope($errorMsg); -// default: -// $this->log->error($errorMsg, $this->getErrorContext()); -// throw new Bitrix24ApiException($errorMsg); - } - $this->logger->debug('handleApiLevelErrors.finish'); - } } \ No newline at end of file diff --git a/tests/Integration/Core/OperatingTimingTest.php b/tests/Integration/Core/OperatingTimingTest.php new file mode 100644 index 00000000..9eebac8b --- /dev/null +++ b/tests/Integration/Core/OperatingTimingTest.php @@ -0,0 +1,153 @@ +getContactsUpdateCommand(15000); + + + //todo считать количество контактов для обновления и считать количество контактов которые обновили, если не совпало, то падаем с ошибкой + + // обновляем контакты в батч-режиме + $cnt = 0; + foreach ($this->contactService->batch->update($contactsToUpdate) as $b24ContactId => $contactUpdateResult) { + $cnt++; + + $debugOperatingLog = sprintf( + 'cnt %s |id %s |operating %s |cur_time %s |op_reset_at %s → %s', + $cnt, + $b24ContactId, + $contactUpdateResult->getResponseData()->getTime()->getOperating(), + $contactUpdateResult->getResponseData()->getTime()->getDateFinish()->format('Y-m-d H:i:s'), + $contactUpdateResult->getResponseData()->getTime()->getOperatingResetAt(), + (new DateTime)->setTimestamp($contactUpdateResult->getResponseData()->getTime()->getOperatingResetAt())->format('Y-m-d H:i:s') + ) . PHP_EOL; + file_put_contents('operating.log', $debugOperatingLog); + } + + $this->assertEquals( + count($contactsToUpdate), + $cnt, + sprintf('updated contacts count %s not equal to expected %s cmd items', $cnt, count($contactsToUpdate)) + ); + + // шаг 1 - выброс корректного исключения, что мол упали из за блокировки метода + // проблемы: - можно потерять часть данных при обновлении, т.к. мы не знаем, какие контакты в клиентском коде обновились, а какие нет или знаем? + +// todo уточнение, по возможности возвращать в исключении остаток данных, которые не успели обновиться + +//[2023-04-15T14:17:57.881428+00:00] integration-test.INFO: getResponseData.responseBody {"responseBody": +//{"result": +//{ +// "result":[], +// "result_error": +// { +// "592dcd1e-cd14-410f-bab5-76b3ede717dd": +// { +// "error":"OPERATION_TIME_LIMIT", +// "error_description":"Method is blocked due to operation time limit." +// } +// }, +// "result_total":[], +// "result_next":[], +// "result_time":[]}, +// "time":{ +// "start":1681568278.071253, +// "finish":1681568278.097257, +// "duration":0.02600383758544922, +// "processing":0.0005891323089599609, +// "date_start":"2023-04-15T17:17:58+03:00", +// "date_finish":"2023-04-15T17:17:58+03:00", +// "operating_reset_at":1681568878, +// "operating":0 +// } +//} +//} {"file":"/Users/mesilov/work/msk03-dev/loyalty/bitrix24-php-sdk/src/Core/Response/Response.php","line":92,"class":"Bitrix24\\SDK\\Core\\Response\\Response","function":"getResponseData","memory_usage":"36 MB"} +//[2023-04-15T14:17:57.881514+00:00] integration-test.DEBUG: handleApiLevelErrors.start [] {"file":"/Users/mesilov/work/msk03-dev/loyalty/bitrix24-php-sdk/src/Core/Response/Response.php","line":152,"class":"Bitrix24\\SDK\\Core\\Response\\Response","function":"handleApiLevelErrors","memory_usage":"36 MB"} +//[2023-04-15T14:17:57.881645+00:00] integration-test.DEBUG: handleApiLevelErrors.finish [] {"file":"/Users/mesilov/work/msk03-dev/loyalty/bitrix24-php-sdk/src/Core/Response/Response.php","line":190,"class":"Bitrix24\\SDK\\Core\\Response\\Response","function":"handleApiLevelErrors","memory_usage":"36 MB"} +//[2023-04-15T14:17:57.881795+00:00] integration-test.DEBUG: getResponseData.parseResponse.finish [] +//[2023-04-15T14:37:47.371152+00:00] integration-test.INFO: getResponseData.responseBody {"responseBody":{"result":{"result":[],"result_error":{"f26b4ebc-3a82-4fe6-8d26-595d6eaf029b":{"error":"OPERATION_TIME_LIMIT","error_description":"Method is blocked due to operation time limit."}},"result_total":[],"result_next":[],"result_time":[]},"time":{"start":1681569467.49515,"finish":1681569467.519364,"duration":0.02421402931213379,"processing":0.0005979537963867188,"date_start":"2023-04-15T17:37:47+03:00","date_finish":"2023-04-15T17:37:47+03:00","operating_reset_at":1681570067,"operating":0}}} {"file":"/Users/mesilov/work/msk03-dev/loyalty/bitrix24-php-sdk/src/Core/Response/Response.php","line":92,"class":"Bitrix24\\SDK\\Core\\Response\\Response","function":"getResponseData","memory_usage":"36 MB"} +//[2023-04-15T14:37:47.371279+00:00] integration-test.DEBUG: handleApiLevelErrors.start [] {"file":"/Users/mesilov/work/msk03-dev/loyalty/bitrix24-php-sdk/src/Core/Response/Response.php","line":152,"class":"Bitrix24\\SDK\\Core\\Response\\Response","function":"handleApiLevelErrors","memory_usage":"36 MB"} + + + // шаг 2 - сделать отдельные стратегии с логикой для батча и придумать, как может быть + // - 2.1 ожидание разблокировки метода без завершения работы батча, т.е. скрипт будет висеть 10 минут, потом попробует продолжить работу, такое можно делать толкьо осознавая последсвия + // - 2.2 выброс события \ вызов обработчика за N секунд до блокировки метода, т.е делегируем логику обработки в клиентский код + + + } + + /** + * Get contacts for update command + * + * @param int $contactsToUpdateCount + * @return array + * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + */ + private function getContactsUpdateCommand(int $contactsToUpdateCount): array + { + $filter = ['>ID' => '1']; + + $contactsCount = $this->contactService->countByFilter($filter); + if ($contactsCount < $contactsToUpdateCount) { + throw new RuntimeException(sprintf('Not enough contacts for test. Need %s, but have %s', $contactsToUpdateCount, $contactsCount)); + } + + $contactsToUpdate = []; + foreach ($this->contactService->batch->list([], $filter, ['ID', 'COMMENTS'], $contactsToUpdateCount) as $b24Contact) { + $contactsToUpdate[$b24Contact->ID] = [ + 'fields' => [ + 'COMMENTS' => $b24Contact->COMMENTS . time() . PHP_EOL, + ], + 'params' => [], + ]; + } + return $contactsToUpdate; + } + + public function setUp(): void + { + $this->batch = Fabric::getBatchService(); + $this->contactService = Fabric::getServiceBuilder()->getCRMScope()->contact(); + } +} \ No newline at end of file diff --git a/tests/Integration/OperatingTimingTest/OperatingTimingTest.php b/tests/Integration/OperatingTimingTest/OperatingTimingTest.php deleted file mode 100644 index d83e6ea3..00000000 --- a/tests/Integration/OperatingTimingTest/OperatingTimingTest.php +++ /dev/null @@ -1,59 +0,0 @@ -contactService->batch->list([], ['>ID' => '12'], ['ID', 'PHONE'], 30000) as $contactList) { - $contactsToUpdate[$contactList->ID] = [ - 'fields' => [ - 'PHONE' => [['ID' => $contactList->PHONE[0]['ID']]] - ], - 'params' => [], - ]; - $contactListId[] = $contactList->ID; - } - foreach ($this->contactService->batch->update($contactsToUpdate) as $dealUpdateResult) { - $logOperating[] = $dealUpdateResult->getResponseData()->getTime()->getOperating(); - $logOperatingResetAt = $dealUpdateResult->getResponseData()->getTime()->getOperatingResetAt(); - $sumOperating = array_sum($logOperating); - echo "summa operating: " . $sumOperating . PHP_EOL; - echo "operating rest at: " . $logOperatingResetAt . PHP_EOL; - } - $timeEnd = microtime(true); - echo sprintf('batch query duration: %s seconds', round($timeEnd - $timeStart, 2)) . PHP_EOL; - - self::assertGreaterThanOrEqual(5, $contactListId); - - } - - public function setUp(): void - { - $this->batch = Fabric::getBatchService(); - $this->contactService = Fabric::getServiceBuilder()->getCRMScope()->contact(); - } -} \ No newline at end of file diff --git a/tests/Unit/Core/ApiLevelErrorHandlerTest.php b/tests/Unit/Core/ApiLevelErrorHandlerTest.php new file mode 100644 index 00000000..f8a96fee --- /dev/null +++ b/tests/Unit/Core/ApiLevelErrorHandlerTest.php @@ -0,0 +1,51 @@ +expectException(OperationTimeLimitExceededException::class); + + $response = [ + 'result' => [ + 'result' => [], + 'result_error' => [ + "592dcd1e-cd14-410f-bab5-76b3ede717dd" => [ + 'error' => 'OPERATION_TIME_LIMIT', + 'error_description' => 'Method is blocked due to operation time limit.' + ] + ] + ], + ]; + + $this->apiLevelErrorHandler->handle($response); + } + + public function setUp(): void + { + $this->apiLevelErrorHandler = new ApiLevelErrorHandler(new NullLogger()); + } +} diff --git a/tests/Unit/Stubs/NullCore.php b/tests/Unit/Stubs/NullCore.php index 493da934..209c064b 100644 --- a/tests/Unit/Stubs/NullCore.php +++ b/tests/Unit/Stubs/NullCore.php @@ -5,6 +5,7 @@ namespace Bitrix24\SDK\Tests\Unit\Stubs; use Bitrix24\SDK\Core\ApiClient; +use Bitrix24\SDK\Core\ApiLevelErrorHandler; use Bitrix24\SDK\Core\Commands\Command; use Bitrix24\SDK\Core\Contracts\ApiClientInterface; use Bitrix24\SDK\Core\Contracts\CoreInterface; @@ -24,14 +25,14 @@ class NullCore implements CoreInterface { /** * @param string $apiMethod - * @param array $parameters + * @param array $parameters * * @return Response * @throws \Exception */ public function call(string $apiMethod, array $parameters = []): Response { - return new Response(new MockResponse(''), new Command('', []), new NullLogger()); + return new Response(new MockResponse(''), new Command('', []), new ApiLevelErrorHandler(new NullLogger()), new NullLogger()); } public function getApiClient(): ApiClientInterface From b4c54b5b0d222a729992c10f7ec67f1b5479714d Mon Sep 17 00:00:00 2001 From: mesilov Date: Sun, 6 Aug 2023 17:49:06 +0400 Subject: [PATCH 61/94] fix errors after refactoring Signed-off-by: mesilov --- src/Core/ApiLevelErrorHandler.php | 13 +++-- .../Core => Temp}/OperatingTimingTest.php | 54 +++++++++---------- 2 files changed, 35 insertions(+), 32 deletions(-) rename tests/{Integration/Core => Temp}/OperatingTimingTest.php (83%) diff --git a/src/Core/ApiLevelErrorHandler.php b/src/Core/ApiLevelErrorHandler.php index 39b1d5df..b25deaee 100644 --- a/src/Core/ApiLevelErrorHandler.php +++ b/src/Core/ApiLevelErrorHandler.php @@ -21,6 +21,8 @@ class ApiLevelErrorHandler { protected LoggerInterface $logger; protected const ERROR_KEY = 'error'; + protected const RESULT_KEY = 'result'; + protected const RESULT_ERROR_KEY = 'result_error'; protected const ERROR_DESCRIPTION_KEY = 'error_description'; /** @@ -41,17 +43,18 @@ public function __construct(LoggerInterface $logger) */ public function handle(array $responseBody): void { - //ошибка единичного запроса + // single query error response if (array_key_exists(self::ERROR_KEY, $responseBody) && array_key_exists(self::ERROR_DESCRIPTION_KEY, $responseBody)) { $this->handleError($responseBody); } - // ошибка в батче - if (!array_key_exists('result', $responseBody)) { + // error in batch response + if (!array_key_exists(self::RESULT_KEY, $responseBody) || (!is_array($responseBody[self::RESULT_KEY]))) { return; } - if (array_key_exists('result_error', $responseBody['result'])) { - foreach ($responseBody['result']['result_error'] as $cmdId => $errorData) { + + if (array_key_exists(self::RESULT_ERROR_KEY, $responseBody[self::RESULT_KEY])) { + foreach ($responseBody[self::RESULT_KEY][self::RESULT_ERROR_KEY] as $cmdId => $errorData) { $this->handleError($errorData, $cmdId); } } diff --git a/tests/Integration/Core/OperatingTimingTest.php b/tests/Temp/OperatingTimingTest.php similarity index 83% rename from tests/Integration/Core/OperatingTimingTest.php rename to tests/Temp/OperatingTimingTest.php index 9eebac8b..50ffbb87 100644 --- a/tests/Integration/Core/OperatingTimingTest.php +++ b/tests/Temp/OperatingTimingTest.php @@ -43,33 +43,33 @@ public function testOperatingTiming(): void //1510 |operating 104.5405356884 |cur_time 2023-04-15 16:57:21 |op_reset_at 1681567640 → 2023-04-15 14:07:20 - $contactsToUpdate = $this->getContactsUpdateCommand(15000); - - - //todo считать количество контактов для обновления и считать количество контактов которые обновили, если не совпало, то падаем с ошибкой - - // обновляем контакты в батч-режиме - $cnt = 0; - foreach ($this->contactService->batch->update($contactsToUpdate) as $b24ContactId => $contactUpdateResult) { - $cnt++; - - $debugOperatingLog = sprintf( - 'cnt %s |id %s |operating %s |cur_time %s |op_reset_at %s → %s', - $cnt, - $b24ContactId, - $contactUpdateResult->getResponseData()->getTime()->getOperating(), - $contactUpdateResult->getResponseData()->getTime()->getDateFinish()->format('Y-m-d H:i:s'), - $contactUpdateResult->getResponseData()->getTime()->getOperatingResetAt(), - (new DateTime)->setTimestamp($contactUpdateResult->getResponseData()->getTime()->getOperatingResetAt())->format('Y-m-d H:i:s') - ) . PHP_EOL; - file_put_contents('operating.log', $debugOperatingLog); - } - - $this->assertEquals( - count($contactsToUpdate), - $cnt, - sprintf('updated contacts count %s not equal to expected %s cmd items', $cnt, count($contactsToUpdate)) - ); +// $contactsToUpdate = $this->getContactsUpdateCommand(15000); +// +// +// //todo считать количество контактов для обновления и считать количество контактов которые обновили, если не совпало, то падаем с ошибкой +// +// // обновляем контакты в батч-режиме +// $cnt = 0; +// foreach ($this->contactService->batch->update($contactsToUpdate) as $b24ContactId => $contactUpdateResult) { +// $cnt++; +// +// $debugOperatingLog = sprintf( +// 'cnt %s |id %s |operating %s |cur_time %s |op_reset_at %s → %s', +// $cnt, +// $b24ContactId, +// $contactUpdateResult->getResponseData()->getTime()->getOperating(), +// $contactUpdateResult->getResponseData()->getTime()->getDateFinish()->format('Y-m-d H:i:s'), +// $contactUpdateResult->getResponseData()->getTime()->getOperatingResetAt(), +// (new DateTime)->setTimestamp($contactUpdateResult->getResponseData()->getTime()->getOperatingResetAt())->format('Y-m-d H:i:s') +// ) . PHP_EOL; +// file_put_contents('operating.log', $debugOperatingLog); +// } +// +// $this->assertEquals( +// count($contactsToUpdate), +// $cnt, +// sprintf('updated contacts count %s not equal to expected %s cmd items', $cnt, count($contactsToUpdate)) +// ); // шаг 1 - выброс корректного исключения, что мол упали из за блокировки метода // проблемы: - можно потерять часть данных при обновлении, т.к. мы не знаем, какие контакты в клиентском коде обновились, а какие нет или знаем? From 324b587dd83e6facd6afae4bcff951d46564a301 Mon Sep 17 00:00:00 2001 From: mesilov Date: Tue, 8 Aug 2023 02:48:27 +0400 Subject: [PATCH 62/94] add batch support for crm-items Signed-off-by: mesilov --- src/Core/Batch.php | 273 +++++++++++------- .../Contracts/BatchOperationsInterface.php | 9 +- 2 files changed, 166 insertions(+), 116 deletions(-) diff --git a/src/Core/Batch.php b/src/Core/Batch.php index d4123b87..4c30f8c7 100644 --- a/src/Core/Batch.php +++ b/src/Core/Batch.php @@ -34,7 +34,7 @@ class Batch implements BatchOperationsInterface /** * Batch constructor. * - * @param CoreInterface $core + * @param CoreInterface $core * @param LoggerInterface $log */ public function __construct(CoreInterface $core, LoggerInterface $log) @@ -62,7 +62,7 @@ protected function clearCommands(): void /** * Add entity items with batch call * - * @param string $apiMethod + * @param string $apiMethod * @param array $entityItems * * @return Generator|ResponseData[] @@ -73,7 +73,7 @@ public function addEntityItems(string $apiMethod, array $entityItems): Generator $this->logger->debug( 'addEntityItems.start', [ - 'apiMethod' => $apiMethod, + 'apiMethod' => $apiMethod, 'entityItems' => $entityItems, ] ); @@ -105,7 +105,7 @@ public function addEntityItems(string $apiMethod, array $entityItems): Generator /** * Delete entity items with batch call * - * @param string $apiMethod + * @param string $apiMethod * @param array $entityItemId * * @return Generator|ResponseData[] @@ -116,7 +116,7 @@ public function deleteEntityItems(string $apiMethod, array $entityItemId): Gener $this->logger->debug( 'deleteEntityItems.start', [ - 'apiMethod' => $apiMethod, + 'apiMethod' => $apiMethod, 'entityItems' => $entityItemId, ] ); @@ -173,7 +173,7 @@ public function deleteEntityItems(string $apiMethod, array $entityItemId): Gener * 'params' => [] // optional fields * ] * - * @param string $apiMethod + * @param string $apiMethod * @param array> $entityItems * * @return Generator|ResponseData[] @@ -184,7 +184,7 @@ public function updateEntityItems(string $apiMethod, array $entityItems): Genera $this->logger->debug( 'updateEntityItems.start', [ - 'apiMethod' => $apiMethod, + 'apiMethod' => $apiMethod, 'entityItems' => $entityItems, ] ); @@ -208,7 +208,7 @@ public function updateEntityItems(string $apiMethod, array $entityItems): Genera } $this->registerCommand($apiMethod, [ - 'id' => $entityItemId, + 'id' => $entityItemId, 'fields' => $entityItem['fields'], 'params' => $entityItem['params'], ]); @@ -244,24 +244,25 @@ public function updateEntityItems(string $apiMethod, array $entityItems): Genera /** * Register api command to command collection for batch calls * - * @param string $apiMethod + * @param string $apiMethod * @param array $parameters - * @param string|null $commandName - * @param callable|null $callback not implemented + * @param string|null $commandName + * @param callable|null $callback not implemented * * @throws \Exception */ protected function registerCommand( - string $apiMethod, - array $parameters = [], - ?string $commandName = null, + string $apiMethod, + array $parameters = [], + ?string $commandName = null, callable $callback = null - ): void { + ): void + { $this->logger->debug( 'registerCommand.start', [ - 'apiMethod' => $apiMethod, - 'parameters' => $parameters, + 'apiMethod' => $apiMethod, + 'parameters' => $parameters, 'commandName' => $commandName, ] ); @@ -317,11 +318,11 @@ protected function getReverseOrder(array $order): array /** * Get traversable list without count elements * - * @param string $apiMethod + * @param string $apiMethod * @param array $order - * @param array $filter - * @param array $select - * @param int|null $limit + * @param array $filter + * @param array $select + * @param int|null $limit * * @return \Generator * @throws \Bitrix24\SDK\Core\Exceptions\BaseException @@ -333,19 +334,22 @@ protected function getReverseOrder(array $order): array */ public function getTraversableList( string $apiMethod, - array $order, - array $filter, - array $select, - ?int $limit = null - ): Generator { + array $order, + array $filter, + array $select, + ?int $limit = null, + ?array $additionalParameters = null + ): Generator + { $this->logger->debug( 'getTraversableList.start', [ 'apiMethod' => $apiMethod, - 'order' => $order, - 'filter' => $filter, - 'select' => $select, - 'limit' => $limit, + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'limit' => $limit, + 'additionalParameters' => $additionalParameters, ] ); @@ -376,15 +380,28 @@ public function getTraversableList( // todo проверили, что если есть limit, то он >1 // todo проверили, что в фильтре нет поля ID, т.к. мы с ним будем работать - $firstResultPage = $this->core->call( - $apiMethod, - [ - 'order' => $order, - 'filter' => $filter, - 'select' => $select, - 'start' => 0, - ] - ); + $params = [ + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'start' => 0, + ]; + + // data structures for crm.items.* is little different =\ + $isCrmItemsInBatch = false; + if ($additionalParameters !== null) { + if (array_key_exists('entityTypeId', $additionalParameters)) { + $isCrmItemsInBatch = true; + } + $params = array_merge($params, $additionalParameters); + } + + if ($isCrmItemsInBatch) { + $keyId = 'id'; + } else { + $keyId = 'ID'; + } + $firstResultPage = $this->core->call($apiMethod, $params); $totalElementsCount = $firstResultPage->getResponseData()->getPagination()->getTotal(); $this->logger->debug('getTraversableList.totalElementsCount', [ 'totalElementsCount' => $totalElementsCount, @@ -407,31 +424,46 @@ public function getTraversableList( // filtered elements count more than one result page(50 elements) // return first page $lastElementIdInFirstPage = null; - foreach ($firstResultPage->getResponseData()->getResult() as $cnt => $listElement) { - $elementsCounter++; - $lastElementIdInFirstPage = (int)$listElement['ID']; - if ($limit !== null && $elementsCounter > $limit) { - return; + if ($isCrmItemsInBatch) { + foreach ($firstResultPage->getResponseData()->getResult()['items'] as $cnt => $listElement) { + $elementsCounter++; + $lastElementIdInFirstPage = (int)$listElement[$keyId]; + if ($limit !== null && $elementsCounter > $limit) { + return; + } + yield $listElement; + } + } else { + foreach ($firstResultPage->getResponseData()->getResult() as $cnt => $listElement) { + $elementsCounter++; + $lastElementIdInFirstPage = (int)$listElement[$keyId]; + if ($limit !== null && $elementsCounter > $limit) { + return; + } + yield $listElement; } - yield $listElement; } $this->clearCommands(); - if (!in_array('ID', $select, true)) { - $select[] = 'ID'; + if (!in_array($keyId, $select, true)) { + $select[] = $keyId; } - // getLastElementId in filtered result - $lastResultPage = $this->core->call( - $apiMethod, - [ - 'order' => $this->getReverseOrder($order), - 'filter' => $filter, - 'select' => $select, - 'start' => 0, - ] - ); - $lastElementId = (int)$lastResultPage->getResponseData()->getResult()[0]['ID']; + $params = [ + 'order' => $this->getReverseOrder($order), + 'filter' => $filter, + 'select' => $select, + 'start' => 0, + ]; + if ($additionalParameters !== null) { + $params = array_merge($params, $additionalParameters); + } + $lastResultPage = $this->core->call($apiMethod, $params); + if ($isCrmItemsInBatch) { + $lastElementId = (int)$lastResultPage->getResponseData()->getResult()['items'][0][$keyId]; + } else { + $lastElementId = (int)$lastResultPage->getResponseData()->getResult()[0][$keyId]; + } // reverse order if you need if ($lastElementIdInFirstPage > $lastElementId) { $tmp = $lastElementIdInFirstPage; @@ -440,7 +472,7 @@ public function getTraversableList( } $this->logger->debug('getTraversableList.lastElementsId', [ 'lastElementIdInFirstPage' => $lastElementIdInFirstPage, - 'lastElementId' => $lastElementId, + 'lastElementId' => $lastElementId, ]); // register commands with updated filter @@ -448,9 +480,9 @@ public function getTraversableList( $lastElementIdInFirstPage++; for ($startId = $lastElementIdInFirstPage; $startId <= $lastElementId; $startId += self::MAX_ELEMENTS_IN_PAGE) { $this->logger->debug('registerCommand.item', [ - 'startId' => $startId, + 'startId' => $startId, 'lastElementId' => $lastElementId, - 'delta' => $lastElementId - $startId, + 'delta' => $lastElementId - $startId, ]); $delta = $lastElementId - $startId; @@ -465,15 +497,17 @@ public function getTraversableList( $isLastPage = true; } - $this->registerCommand( - $apiMethod, - [ - 'order' => [], - 'filter' => $this->updateFilterForBatch($startId, $lastElementIdInPage, $isLastPage, $filter), - 'select' => $select, - 'start' => -1, - ] - ); + $params = [ + 'order' => [], + 'filter' => $this->updateFilterForBatch($keyId, $startId, $lastElementIdInPage, $isLastPage, $filter), + 'select' => $select, + 'start' => -1, + ]; + if ($additionalParameters !== null) { + $params = array_merge($params, $additionalParameters); + } + + $this->registerCommand($apiMethod, $params); } $this->logger->debug( 'getTraversableList.commandsRegistered', @@ -492,44 +526,58 @@ public function getTraversableList( 'getTraversableList.batchResultItem', [ 'batchCommandItemNumber' => $queryCnt, - 'nextItem' => $queryResultData->getPagination()->getNextItem(), - 'durationTime' => $queryResultData->getTime()->getDuration(), + 'nextItem' => $queryResultData->getPagination()->getNextItem(), + 'durationTime' => $queryResultData->getTime()->getDuration(), ] ); + // iterate items in batch query result - foreach ($queryResultData->getResult() as $cnt => $listElement) { - $elementsCounter++; - if ($limit !== null && $elementsCounter > $limit) { - return; + if ($isCrmItemsInBatch) { + foreach ($queryResultData->getResult()['items'] as $cnt => $listElement) { + $elementsCounter++; + if ($limit !== null && $elementsCounter > $limit) { + return; + } + yield $listElement; + } + } else { + foreach ($queryResultData->getResult() as $cnt => $listElement) { + $elementsCounter++; + if ($limit !== null && $elementsCounter > $limit) { + return; + } + yield $listElement; } - yield $listElement; } + } $this->logger->debug('getTraversableList.finish'); } /** - * @param int $startElementId - * @param int $lastElementId - * @param bool $isLastPage + * @param string $keyId + * @param int $startElementId + * @param int $lastElementId + * @param bool $isLastPage * @param array $oldFilter * * @return array */ - protected function updateFilterForBatch(int $startElementId, int $lastElementId, bool $isLastPage, array $oldFilter): array + protected function updateFilterForBatch(string $keyId, int $startElementId, int $lastElementId, bool $isLastPage, array $oldFilter): array { $this->logger->debug('updateFilterForBatch.start', [ 'startElementId' => $startElementId, - 'lastElementId' => $lastElementId, - 'isLastPage' => $isLastPage, - 'oldFilter' => $oldFilter, + 'lastElementId' => $lastElementId, + 'isLastPage' => $isLastPage, + 'oldFilter' => $oldFilter, + 'key' => $keyId, ]); $filter = array_merge( $oldFilter, [ - '>=ID' => $startElementId, - $isLastPage ? '<=ID' : ' $lastElementId, + sprintf('>=%s', $keyId) => $startElementId, + $isLastPage ? sprintf('<=%s', $keyId) : sprintf('<%s', $keyId) => $lastElementId, ] ); $this->logger->debug('updateFilterForBatch.finish', [ @@ -544,11 +592,11 @@ protected function updateFilterForBatch(int $startElementId, int $lastElementId, * * work with start item position and elements count * - * @param string $apiMethod + * @param string $apiMethod * @param array $order - * @param array $filter - * @param array $select - * @param int|null $limit + * @param array $filter + * @param array $select + * @param int|null $limit * * @return Generator * @throws BaseException @@ -561,19 +609,20 @@ protected function updateFilterForBatch(int $startElementId, int $lastElementId, */ public function getTraversableListWithCount( string $apiMethod, - array $order, - array $filter, - array $select, - ?int $limit = null - ): Generator { + array $order, + array $filter, + array $select, + ?int $limit = null + ): Generator + { $this->logger->debug( 'getTraversableListWithCount.start', [ 'apiMethod' => $apiMethod, - 'order' => $order, - 'filter' => $filter, - 'select' => $select, - 'limit' => $limit, + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'limit' => $limit, ] ); $this->clearCommands(); @@ -582,10 +631,10 @@ public function getTraversableListWithCount( $firstResult = $this->core->call( $apiMethod, [ - 'order' => $order, + 'order' => $order, 'filter' => $filter, 'select' => $select, - 'start' => 0, + 'start' => 0, ] ); @@ -595,7 +644,7 @@ public function getTraversableListWithCount( $this->logger->debug( 'getTraversableListWithCount.calculateCommandsRange', [ - 'nextItem' => $nextItem, + 'nextItem' => $nextItem, 'totalItems' => $total, ] ); @@ -606,10 +655,10 @@ public function getTraversableListWithCount( $this->registerCommand( $apiMethod, [ - 'order' => $order, + 'order' => $order, 'filter' => $filter, 'select' => $select, - 'start' => $startItem, + 'start' => $startItem, ] ); if ($limit !== null && $limit < $startItem) { @@ -621,10 +670,10 @@ public function getTraversableListWithCount( $this->registerCommand( $apiMethod, [ - 'order' => $order, + 'order' => $order, 'filter' => $filter, 'select' => $select, - 'start' => 0, + 'start' => 0, ] ); } @@ -632,7 +681,7 @@ public function getTraversableListWithCount( $this->logger->debug( 'getTraversableListWithCount.commandsRegistered', [ - 'commandsCount' => $this->commands->count(), + 'commandsCount' => $this->commands->count(), 'totalItemsToSelect' => $total, ] ); @@ -647,8 +696,8 @@ public function getTraversableListWithCount( 'getTraversableListWithCount.batchResultItem', [ 'batchCommandItemNumber' => $queryCnt, - 'nextItem' => $queryResultData->getPagination()->getNextItem(), - 'durationTime' => $queryResultData->getTime()->getDuration(), + 'nextItem' => $queryResultData->getPagination()->getNextItem(), + 'durationTime' => $queryResultData->getTime()->getDuration(), ] ); // iterate items in batch query result @@ -692,8 +741,8 @@ protected function getTraversable(bool $isHaltOnError): Generator $this->logger->debug( 'getTraversable.batchResultItem.processStart', [ - 'batchItemNumber' => $batchItem, - 'batchApiCommand' => $batchResult->getApiCommand()->getApiMethod(), + 'batchItemNumber' => $batchItem, + 'batchApiCommand' => $batchResult->getApiCommand()->getApiMethod(), 'batchApiCommandUuid' => $batchResult->getApiCommand()->getUuid()->toString(), ] ); @@ -765,7 +814,7 @@ private function getTraversableBatchResults(bool $isHaltOnError): Generator 'getTraversableBatchResults.batchQuery', [ 'batchQueryNumber' => $batchQueryCounter, - 'queriesCount' => count($batchQuery), + 'queriesCount' => count($batchQuery), ] ); // batch call diff --git a/src/Core/Contracts/BatchOperationsInterface.php b/src/Core/Contracts/BatchOperationsInterface.php index 801beab6..f6e5b55c 100644 --- a/src/Core/Contracts/BatchOperationsInterface.php +++ b/src/Core/Contracts/BatchOperationsInterface.php @@ -18,12 +18,12 @@ interface BatchOperationsInterface /** * Batch wrapper for *.list methods without counting elements on every api-call * - * @param string $apiMethod + * @param string $apiMethod * @param array $order * @param array $filter * @param array $select - * @param int|null $limit - * + * @param int|null $limit + * @param array|null $additionalParameters * @return Generator * @throws BaseException */ @@ -32,7 +32,8 @@ public function getTraversableList( array $order, array $filter, array $select, - ?int $limit = null + ?int $limit = null, + ?array $additionalParameters = null ): Generator; /** From 8243632c6ac281d9173aab491ff357cc14cb3999 Mon Sep 17 00:00:00 2001 From: mesilov Date: Sun, 13 Aug 2023 19:06:06 +0400 Subject: [PATCH 63/94] add crm-item with batch support Signed-off-by: mesilov --- CHANGELOG.md | 1 + src/Services/CRM/CRMServiceBuilder.php | 62 ++----- .../CRM/Common/Result/AbstractCrmItem.php | 51 ++++-- .../CRM/Item/Result/ItemItemResult.php | 53 ++++++ src/Services/CRM/Item/Result/ItemResult.php | 16 ++ src/Services/CRM/Item/Result/ItemsResult.php | 25 +++ src/Services/CRM/Item/Service/Batch.php | 68 ++++++++ src/Services/CRM/Item/Service/Item.php | 158 ++++++++++++++++++ 8 files changed, 372 insertions(+), 62 deletions(-) create mode 100644 src/Services/CRM/Item/Result/ItemItemResult.php create mode 100644 src/Services/CRM/Item/Result/ItemResult.php create mode 100644 src/Services/CRM/Item/Result/ItemsResult.php create mode 100644 src/Services/CRM/Item/Service/Batch.php create mode 100644 src/Services/CRM/Item/Service/Item.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 34531975..dbd28db7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ * `Bitrix24\SDK\Services\User\Service\User::get` - get user * `Bitrix24\SDK\Services\User\Service\User::update` - update user * `Bitrix24\SDK\Services\User\Service\User::search` - search users +* add [crm item support](https://github.com/mesilov/bitrix24-php-sdk/issues/330) ### Changed diff --git a/src/Services/CRM/CRMServiceBuilder.php b/src/Services/CRM/CRMServiceBuilder.php index 7bd84b03..f78a34ae 100644 --- a/src/Services/CRM/CRMServiceBuilder.php +++ b/src/Services/CRM/CRMServiceBuilder.php @@ -5,22 +5,9 @@ namespace Bitrix24\SDK\Services\CRM; use Bitrix24\SDK\Services\AbstractServiceBuilder; -use Bitrix24\SDK\Services\CRM\Contact; -use Bitrix24\SDK\Services\CRM\Deal; -use Bitrix24\SDK\Services\CRM\Product; -use Bitrix24\SDK\Services\CRM\Settings; - -/** - * Class CRMServiceBuilder - * - * @package Bitrix24\SDK\Services\CRM - */ class CRMServiceBuilder extends AbstractServiceBuilder { - /** - * @return Settings\Service\Settings - */ public function settings(): Settings\Service\Settings { if (!isset($this->serviceCache[__METHOD__])) { @@ -30,9 +17,6 @@ public function settings(): Settings\Service\Settings return $this->serviceCache[__METHOD__]; } - /** - * @return Deal\Service\DealContact - */ public function dealContact(): Deal\Service\DealContact { if (!isset($this->serviceCache[__METHOD__])) { @@ -42,9 +26,6 @@ public function dealContact(): Deal\Service\DealContact return $this->serviceCache[__METHOD__]; } - /** - * @return Deal\Service\DealCategory - */ public function dealCategory(): Deal\Service\DealCategory { if (!isset($this->serviceCache[__METHOD__])) { @@ -54,9 +35,6 @@ public function dealCategory(): Deal\Service\DealCategory return $this->serviceCache[__METHOD__]; } - /** - * @return Deal\Service\Deal - */ public function deal(): Deal\Service\Deal { if (!isset($this->serviceCache[__METHOD__])) { @@ -70,9 +48,6 @@ public function deal(): Deal\Service\Deal return $this->serviceCache[__METHOD__]; } - /** - * @return \Bitrix24\SDK\Services\CRM\Deal\Service\DealUserfield - */ public function dealUserfield(): Deal\Service\DealUserfield { if (!isset($this->serviceCache[__METHOD__])) { @@ -85,9 +60,6 @@ public function dealUserfield(): Deal\Service\DealUserfield return $this->serviceCache[__METHOD__]; } - /** - * @return Contact\Service\Contact - */ public function contact(): Contact\Service\Contact { if (!isset($this->serviceCache[__METHOD__])) { @@ -101,9 +73,6 @@ public function contact(): Contact\Service\Contact return $this->serviceCache[__METHOD__]; } - /** - * @return \Bitrix24\SDK\Services\CRM\Contact\Service\ContactUserfield - */ public function contactUserfield(): Contact\Service\ContactUserfield { if (!isset($this->serviceCache[__METHOD__])) { @@ -128,9 +97,6 @@ public function dealProductRows(): Deal\Service\DealProductRows return $this->serviceCache[__METHOD__]; } - /** - * @return Deal\Service\DealCategoryStage - */ public function dealCategoryStage(): Deal\Service\DealCategoryStage { if (!isset($this->serviceCache[__METHOD__])) { @@ -140,9 +106,6 @@ public function dealCategoryStage(): Deal\Service\DealCategoryStage return $this->serviceCache[__METHOD__]; } - /** - * @return Product\Service\Product - */ public function product(): Product\Service\Product { if (!isset($this->serviceCache[__METHOD__])) { @@ -156,9 +119,6 @@ public function product(): Product\Service\Product return $this->serviceCache[__METHOD__]; } - /** - * @return Userfield\Service\Userfield - */ public function userfield(): Userfield\Service\Userfield { if (!isset($this->serviceCache[__METHOD__])) { @@ -171,9 +131,6 @@ public function userfield(): Userfield\Service\Userfield return $this->serviceCache[__METHOD__]; } - /** - * @return Lead\Service\Lead - */ public function lead(): Lead\Service\Lead { if (!isset($this->serviceCache[__METHOD__])) { @@ -187,9 +144,6 @@ public function lead(): Lead\Service\Lead return $this->serviceCache[__METHOD__]; } - /** - * @return Activity\Service\Activity - */ public function activity(): Activity\Service\Activity { if (!isset($this->serviceCache[__METHOD__])) { @@ -203,9 +157,6 @@ public function activity(): Activity\Service\Activity return $this->serviceCache[__METHOD__]; } - /** - * @return Activity\ActivityFetcherBuilder - */ public function activityFetcher(): Activity\ActivityFetcherBuilder { if (!isset($this->serviceCache[__METHOD__])) { @@ -219,4 +170,17 @@ public function activityFetcher(): Activity\ActivityFetcherBuilder return $this->serviceCache[__METHOD__]; } + + public function item(): Item\Service\Item + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Item\Service\Item( + new Item\Service\Batch($this->batch, $this->log), + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } } \ No newline at end of file diff --git a/src/Services/CRM/Common/Result/AbstractCrmItem.php b/src/Services/CRM/Common/Result/AbstractCrmItem.php index eaf28aa5..81467927 100644 --- a/src/Services/CRM/Common/Result/AbstractCrmItem.php +++ b/src/Services/CRM/Common/Result/AbstractCrmItem.php @@ -15,7 +15,7 @@ class AbstractCrmItem extends AbstractItem private const CRM_USERFIELD_PREFIX = 'UF_CRM_'; /** - * @var \Money\Currency + * @var Currency */ private Currency $currency; @@ -25,7 +25,6 @@ public function __construct(array $data, Currency $currency = null) if ($currency !== null) { $this->currency = $currency; } - } /** @@ -49,6 +48,15 @@ public function __get($offset) case 'ASSIGNED_BY_ID': case 'CREATED_BY_ID': case 'MODIFY_BY_ID': + case 'createdBy': + case 'updatedBy': + case 'movedBy': + case 'begindate': + case 'closedate': + case 'opportunity': + case 'opportunityAccount': + case 'taxValueAccount': + case 'taxValue': // deal case 'LEAD_ID': case 'CONTACT_ID': @@ -57,12 +65,20 @@ public function __get($offset) case 'OWNER_ID': // DealCategoryItem case 'SORT': + case 'id': + case 'categoryId': + case 'webformId': + case 'assignedById': + case 'contactId': + case 'lastActivityBy': if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { return (int)$this->data[$offset]; } return null; case 'COMPANY_ID': + case 'companyId': + case 'mycompanyId': if ($this->data[$offset] !== '' && $this->data[$offset] !== null && $this->data[$offset] !== '0') { return (int)$this->data[$offset]; } @@ -73,17 +89,9 @@ public function __get($offset) case 'HAS_EMAIL': case 'HAS_IMOL': case 'OPENED': - // deal - case 'PRICE_EXCLUSIVE': - case 'PRICE_NETTO': - case 'PRICE_BRUTTO': - case 'PRICE': - if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { - $var = $this->data[$offset] * 100; - return new Money((string)$var, new Currency($this->currency->getCode())); - } - return null; + case 'opened': case 'IS_MANUAL_OPPORTUNITY': + case 'isManualOpportunity': case 'CLOSED': case 'IS_NEW': case 'IS_LOCKED': @@ -97,11 +105,28 @@ public function __get($offset) case 'BIRTHDATE': case 'BEGINDATE': case 'CLOSEDATE': + case 'createdTime': + case 'updatedTime': + case 'movedTime': + case 'lastActivityTime': if ($this->data[$offset] !== '') { return DateTimeImmutable::createFromFormat(DATE_ATOM, $this->data[$offset]); } return null; + // deal + case 'PRICE_EXCLUSIVE': + case 'PRICE_NETTO': + case 'PRICE_BRUTTO': + case 'PRICE': + if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { + $var = $this->data[$offset] * 100; + return new Money((string)$var, new Currency($this->currency->getCode())); + } + return null; + case 'currencyId': + case 'accountCurrencyId': + return new Currency($this->data[$offset]); default: return $this->data[$offset] ?? null; } @@ -113,7 +138,7 @@ public function __get($offset) * @param string $fieldName * * @return mixed|null - * @throws \Bitrix24\SDK\Services\CRM\Userfield\Exceptions\UserfieldNotFoundException + * @throws UserfieldNotFoundException */ protected function getKeyWithUserfieldByFieldName(string $fieldName) { diff --git a/src/Services/CRM/Item/Result/ItemItemResult.php b/src/Services/CRM/Item/Result/ItemItemResult.php new file mode 100644 index 00000000..3d7090a8 --- /dev/null +++ b/src/Services/CRM/Item/Result/ItemItemResult.php @@ -0,0 +1,53 @@ +getCoreResponse()->getResponseData()->getResult()['item']); + } +} \ No newline at end of file diff --git a/src/Services/CRM/Item/Result/ItemsResult.php b/src/Services/CRM/Item/Result/ItemsResult.php new file mode 100644 index 00000000..978ffc0d --- /dev/null +++ b/src/Services/CRM/Item/Result/ItemsResult.php @@ -0,0 +1,25 @@ +getCoreResponse()->getResponseData()->getResult() as $item) { + $items[] = new ItemItemResult($item); + } + + return $items; + } +} \ No newline at end of file diff --git a/src/Services/CRM/Item/Service/Batch.php b/src/Services/CRM/Item/Service/Batch.php new file mode 100644 index 00000000..14ff222b --- /dev/null +++ b/src/Services/CRM/Item/Service/Batch.php @@ -0,0 +1,68 @@ +batch = $batch; + $this->log = $log; + } + + /** + * Batch list method for crm items + * + * @return Generator + * @throws BaseException + */ + public function list(int $entityTypeId, array $order, array $filter, array $select, ?int $limit = null): Generator + { + $this->log->debug( + 'batchList', + [ + 'entityTypeId' => $entityTypeId, + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'limit' => $limit, + ] + ); + foreach ($this->batch->getTraversableList('crm.item.list', $order, $filter, $select, $limit, ['entityTypeId' => $entityTypeId]) as $key => $value) { + yield $key => new ItemItemResult($value); + } + } + + /** + * Batch adding crm items + * + * @return Generator|ItemItemResult[] + * + * @throws BaseException + */ + public function add(int $entityTypeId, array $items): Generator + { + $rawItems = []; + foreach ($items as $item) { + $rawItems[] = [ + 'entityTypeId' => $entityTypeId, + '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 new file mode 100644 index 00000000..f080641e --- /dev/null +++ b/src/Services/CRM/Item/Service/Item.php @@ -0,0 +1,158 @@ +batch = $batch; + } + + /** + * Method creates new SPA item with entityTypeId. + * + * @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 + */ + public function add(int $entityTypeId, array $fields): ItemResult + { + return new ItemResult( + $this->core->call( + 'crm.item.add', + [ + 'entityTypeId' => $entityTypeId, + 'fields' => $fields, + ] + ) + ); + } + + /** + * Deletes item with id for SPA with entityTypeId. + * + * @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 + */ + public function delete(int $entityTypeId, int $id): DeletedItemResult + { + return new DeletedItemResult( + $this->core->call( + 'crm.item.delete', ['entityTypeId' => $entityTypeId, 'id' => $id] + ) + ); + } + + /** + * Returns the fields data with entityTypeId. + * + * @link https://training.bitrix24.com/rest_help/crm/dynamic/methodscrmitem/crm_item_fields.php + * + * @param int $entityTypeId + * @return FieldsResult + * @throws BaseException + * @throws TransportException + */ + public function fields(int $entityTypeId): FieldsResult + { + return new FieldsResult($this->core->call('crm.item.fields', ['entityTypeId' => $entityTypeId])); + } + + /** + * Returns item data with id for SPA with entityTypeId. + * + * @link https://training.bitrix24.com/rest_help/crm/dynamic/methodscrmitem/crm_item_get.php + * + * @throws BaseException + * @throws TransportException + */ + public function get(int $entityTypeId, int $id): ItemResult + { + return new ItemResult($this->core->call('crm.item.get', ['entityTypeId' => $entityTypeId, 'id' => $id])); + } + + /** + * Returns array with SPA items with entityTypeId + * + * @link https://training.bitrix24.com/rest_help/crm/dynamic/methodscrmitem/crm_item_list.php + * + * @throws BaseException + * @throws TransportException + */ + public function list(int $entityTypeId, array $order, array $filter, array $select, int $startItem = 0): ItemsResult + { + return new ItemsResult( + $this->core->call( + 'crm.item.list', + [ + 'entityTypeId' => $entityTypeId, + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'start' => $startItem, + ] + ) + ); + } + + /** + * Updates the specified (existing) item. + * + * @link https://training.bitrix24.com/rest_help/crm/dynamic/methodscrmitem/crm_item_update.php + * + * @throws BaseException + * @throws TransportException + */ + public function update(int $entityTypeId, int $id, array $fields): UpdatedItemResult + { + return new UpdatedItemResult( + $this->core->call( + 'crm.item.update', + [ + 'entityTypeId' => $entityTypeId, + 'id' => $id, + 'fields' => $fields, + ] + ) + ); + } + + /** + * Count by filter + * + * @throws BaseException + * @throws TransportException + */ + 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 From 631784914743826ecfd6949e7264f4666fe1f19a Mon Sep 17 00:00:00 2001 From: mesilov Date: Mon, 14 Aug 2023 00:55:29 +0400 Subject: [PATCH 64/94] add duplicate service and tests Signed-off-by: mesilov --- CHANGELOG.md | 3 +- src/Services/CRM/CRMServiceBuilder.php | 12 +++ .../CRM/Duplicates/Result/DuplicateResult.php | 50 ++++++++++++ .../CRM/Duplicates/Service/Duplicate.php | 47 +++++++++++ .../CRM/Duplicates/Service/EntityType.php | 12 +++ .../CRM/Duplicates/Service/DuplicateTest.php | 80 +++++++++++++++++++ tests/Unit/Stubs/NullBatch.php | 8 +- 7 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 src/Services/CRM/Duplicates/Result/DuplicateResult.php create mode 100644 src/Services/CRM/Duplicates/Service/Duplicate.php create mode 100644 src/Services/CRM/Duplicates/Service/EntityType.php create mode 100644 tests/Integration/Services/CRM/Duplicates/Service/DuplicateTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index dbd28db7..40b2ac1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,8 @@ * `Bitrix24\SDK\Services\User\Service\User::get` - get user * `Bitrix24\SDK\Services\User\Service\User::update` - update user * `Bitrix24\SDK\Services\User\Service\User::search` - search users -* add [crm item support](https://github.com/mesilov/bitrix24-php-sdk/issues/330) +* add [crm item support](https://github.com/mesilov/bitrix24-php-sdk/issues/330) +* add Duplicate search support for `Bitrix24\SDK\Services\CRM\Duplicates\Service\Duplicate` ### Changed diff --git a/src/Services/CRM/CRMServiceBuilder.php b/src/Services/CRM/CRMServiceBuilder.php index f78a34ae..49678877 100644 --- a/src/Services/CRM/CRMServiceBuilder.php +++ b/src/Services/CRM/CRMServiceBuilder.php @@ -183,4 +183,16 @@ public function item(): Item\Service\Item return $this->serviceCache[__METHOD__]; } + + public function duplicate(): Duplicates\Service\Duplicate + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Duplicates\Service\Duplicate( + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } } \ No newline at end of file diff --git a/src/Services/CRM/Duplicates/Result/DuplicateResult.php b/src/Services/CRM/Duplicates/Result/DuplicateResult.php new file mode 100644 index 00000000..dffe44ad --- /dev/null +++ b/src/Services/CRM/Duplicates/Result/DuplicateResult.php @@ -0,0 +1,50 @@ +getCoreResponse()->getResponseData()->getResult())) { + return false; + } + + if (count($this->getCoreResponse()->getResponseData()->getResult()['CONTACT']) > 1) { + return true; + } + + return false; + } + + public function hasOneContact(): bool + { + if (!array_key_exists('CONTACT', $this->getCoreResponse()->getResponseData()->getResult())) { + return false; + } + + if (count($this->getCoreResponse()->getResponseData()->getResult()['CONTACT']) === 1) { + return true; + } + + return false; + } + + /** + * @return array + * @throws BaseException + */ + public function getContactsId(): array + { + if (!array_key_exists('CONTACT', $this->getCoreResponse()->getResponseData()->getResult())) { + return []; + } + + return $this->getCoreResponse()->getResponseData()->getResult()['CONTACT']; + } +} \ No newline at end of file diff --git a/src/Services/CRM/Duplicates/Service/Duplicate.php b/src/Services/CRM/Duplicates/Service/Duplicate.php new file mode 100644 index 00000000..9b0b122d --- /dev/null +++ b/src/Services/CRM/Duplicates/Service/Duplicate.php @@ -0,0 +1,47 @@ + $phones + * @param EntityType|null $entityType + * @return DuplicateResult + * @throws BaseException + * @throws TransportException + */ + public function findByPhone(array $phones, ?EntityType $entityType = null): mixed + { + return new DuplicateResult($this->core->call('crm.duplicate.findbycomm', + [ + 'type' => 'PHONE', + 'values' => $phones, + 'entity_type' => $entityType?->value + ])); + } + + /** + * @param array $emails + * @param EntityType|null $entityType + * @return DuplicateResult + * @throws BaseException + * @throws TransportException + */ + public function findByEmail(array $emails, ?EntityType $entityType = null): DuplicateResult + { + return new DuplicateResult($this->core->call('crm.duplicate.findbycomm', + [ + 'type' => 'EMAIL', + 'values' => $emails, + 'entity_type' => $entityType?->value + ])); + } +} \ No newline at end of file diff --git a/src/Services/CRM/Duplicates/Service/EntityType.php b/src/Services/CRM/Duplicates/Service/EntityType.php new file mode 100644 index 00000000..3e5583ea --- /dev/null +++ b/src/Services/CRM/Duplicates/Service/EntityType.php @@ -0,0 +1,12 @@ +duplicate->findByEmail([sprintf('%s@gmail.com', time())]); + $this->assertFalse($res->hasDuplicateContacts()); + $this->assertFalse($res->hasOneContact()); + $this->assertCount(0, $res->getContactsId()); + } + + /** + * @return void + * @throws BaseException + * @throws TransportException + * @covers \Bitrix24\SDK\Services\CRM\Duplicates\Service\Duplicate::findByEmail + */ + public function testDuplicatesByEmailOneItemFound(): void + { + $email = sprintf('%s@gmail.com', time()); + $b24ContactId = $this->contactService->add([ + 'NAME' => 'Test', + 'LAST_NAME' => 'Test', + 'EMAIL' => [ + [ + 'VALUE' => $email, + 'TYPE' => 'WORK' + ] + ] + ])->getId(); + + $res = $this->duplicate->findByEmail([$email]); + $this->assertFalse($res->hasDuplicateContacts()); + $this->assertTrue($res->hasOneContact()); + $this->assertCount(1, $res->getContactsId()); + } + + /** + * @return void + * @throws BaseException + * @throws TransportException + * @covers \Bitrix24\SDK\Services\CRM\Duplicates\Service\Duplicate::findByPhone + */ + public function testDuplicatesByPhoneNotFound(): void + { + $res = $this->duplicate->findByPhone([sprintf('+1%s', time())]); + $this->assertFalse($res->hasDuplicateContacts()); + $this->assertFalse($res->hasOneContact()); + $this->assertCount(0, $res->getContactsId()); + } + + + public function setUp(): void + { + $this->contactService = Fabric::getServiceBuilder()->getCRMScope()->contact(); + $this->duplicate = Fabric::getServiceBuilder()->getCRMScope()->duplicate(); + + } +} \ No newline at end of file diff --git a/tests/Unit/Stubs/NullBatch.php b/tests/Unit/Stubs/NullBatch.php index f775dab6..7781fa6f 100644 --- a/tests/Unit/Stubs/NullBatch.php +++ b/tests/Unit/Stubs/NullBatch.php @@ -18,9 +18,15 @@ class NullBatch implements BatchOperationsInterface { /** + * @param string $apiMethod + * @param array $order + * @param array $filter + * @param array $select + * @param int|null $limit + * @param array|null $additionalParameters * @inheritDoc */ - public function getTraversableList(string $apiMethod, array $order, array $filter, array $select, ?int $limit = null): Generator + public function getTraversableList(string $apiMethod, array $order, array $filter, array $select, ?int $limit = null, ?array $additionalParameters = null): Generator { yield []; } From 97c833fa58339c32f924a44f2599babfd92d136c Mon Sep 17 00:00:00 2001 From: mesilov Date: Tue, 22 Aug 2023 01:52:22 +0400 Subject: [PATCH 65/94] add cli util - copy property values Signed-off-by: mesilov --- bin/console | 2 + tools/Commands/CopyPropertyValues.php | 296 ++++++++++++++++++++++++++ 2 files changed, 298 insertions(+) create mode 100644 tools/Commands/CopyPropertyValues.php diff --git a/bin/console b/bin/console index 86ccde03..5a561cd4 100644 --- a/bin/console +++ b/bin/console @@ -1,6 +1,7 @@ #!/usr/bin/env php add(new GenerateContactsCommand($log)); $application->add(new ListCommand($log)); $application->add(new ShowFieldsDescriptionCommand($log)); +$application->add(new CopyPropertyValues($log)); $application->run($input); \ No newline at end of file diff --git a/tools/Commands/CopyPropertyValues.php b/tools/Commands/CopyPropertyValues.php new file mode 100644 index 00000000..d4cd9d8c --- /dev/null +++ b/tools/Commands/CopyPropertyValues.php @@ -0,0 +1,296 @@ +logger = $logger; + parent::__construct(); + } + + /** + * @param OutputInterface $output + * @param Contact $service + * @param array $updateCmd + * @return void + * @throws BaseException + */ + public function updateItems(OutputInterface $output, Contact $service, array $updateCmd): void + { + $progressBar = new ProgressBar($output, count($updateCmd)); + $progressBar->start(); + + foreach ($service->batch->update($updateCmd) as $item) { + $this->logger->debug('updateItems', [ + 'isUpdated' => $item->isSuccess() === true ? 'Y' : 'N', + ]); + $progressBar->advance(); + } + + $progressBar->finish(); + } + + + public function createUpdateCommand(array $data, string $b24SourceProp, string $b24DestinationProp): array + { + $updateCmd = []; + foreach ($data as $id => $item) { + $updateCmd[$id] = [ + 'fields' => [ + $b24DestinationProp =>$item[$b24SourceProp], + ] + ]; + } + + return $updateCmd; + } + + /** + * @param OutputInterface $output + * @param Contact $service + * @param array $filter + * @param string $b24SourceProp + * @param string $b24DestinationProp + * @return array + * @throws BaseException + * @throws \Bitrix24\SDK\Core\Exceptions\TransportException + * @throws \Bitrix24\SDK\Services\CRM\Userfield\Exceptions\UserfieldNotFoundException + */ + public function readDataFromProperties(OutputInterface $output, Contact $service, array $filter, string $b24SourceProp, string $b24DestinationProp): array + { + $progressBar = new ProgressBar($output, $service->countByFilter($filter)); + $progressBar->start(); + + $data = []; + foreach ($service->batch->list([], $filter, ['ID', $b24SourceProp, $b24DestinationProp]) as $id => $item) { + $data[$item->ID] = [ + $b24SourceProp => (string)$item->getUserfieldByFieldName($b24SourceProp), + $b24DestinationProp => (string)$item->getUserfieldByFieldName($b24DestinationProp), + ]; + $progressBar->advance(); + } + $progressBar->finish(); + + return $data; + } + + protected function configure(): void + { + $this + ->setDescription('copy property values from one property to another') + ->setHelp('copy property values from one property to another in same portal') + ->addOption( + self::WEBHOOK_URL, + null, + InputOption::VALUE_REQUIRED, + 'bitrix24 incoming webhook', + '' + ) + ->addOption( + self::SOURCE_PROPERTY, + null, + InputOption::VALUE_REQUIRED, + 'source property id', + + ) + ->addOption( + self::DESTINATION_PROPERTY, + null, + InputOption::VALUE_REQUIRED, + 'destination property id', + ) + ->addOption( + self::ENTITY_TYPE_PROPERTY, + null, + InputOption::VALUE_REQUIRED, + 'entity type: contact, company, lead, deal', + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->logger->debug('CopyPropertyValues.start'); + + $b24Webhook = (string)$input->getOption(self::WEBHOOK_URL); + $b24EntityType = (string)$input->getOption(self::ENTITY_TYPE_PROPERTY); + $b24SourceProp = (string)$input->getOption(self::SOURCE_PROPERTY); + $b24DestinationProp = (string)$input->getOption(self::DESTINATION_PROPERTY); + + $io = new SymfonyStyle($input, $output); + $output->writeln( + [ + 'Copy property values from one property to another', + '========================', + sprintf('webhook url: %s', $b24Webhook), + sprintf('entity type: %s', $b24EntityType), + sprintf('source property: %s', $b24SourceProp), + sprintf('destination property: %s', $b24DestinationProp), + ] + ); + + try { + if ($b24Webhook === '') { + throw new InvalidArgumentException('webhook url is empty'); + } + if ($b24EntityType === '') { + throw new InvalidArgumentException('entity_type is empty'); + } + if ($b24SourceProp === '') { + throw new InvalidArgumentException('source property is empty'); + } + if ($b24DestinationProp === '') { + throw new InvalidArgumentException('destination property is empty'); + } + $sb = (new ServiceBuilderFactory(new EventDispatcher(), $this->logger))->initFromWebhook($b24Webhook); + if (!in_array($b24EntityType, $this->supportedEntityTypes, true)) { + throw new InvalidArgumentException(sprintf('entity type %s is not supported', $b24EntityType)); + } + + $service = null; + switch ($b24EntityType) { + case 'contact': + $fields = $sb->getCRMScope()->contact()->fields(); + $service = $sb->getCRMScope()->contact(); + break; + case 'company': + case 'lead': + case 'deal': + default: + throw new InvalidArgumentException(sprintf('unsupported entity type %s', $b24EntityType)); + } + + if (!array_key_exists($b24SourceProp, $fields->getFieldsDescription())) { + throw new InvalidArgumentException(sprintf('source property «%s» is not found in entity %s', $b24SourceProp, $b24EntityType)); + } + if (!array_key_exists($b24DestinationProp, $fields->getFieldsDescription())) { + throw new InvalidArgumentException(sprintf('destination property «%s» is not found in entity %s', $b24DestinationProp, $b24EntityType)); + } + + // количество элементов c заполненным полем источником + // количество элементов с заполненным полем назначения + // количество элементов у которых заполнено ОБА поля + $totalElementsCnt = $service->countByFilter(); + $elementsWithFilledSourceProp = $service->countByFilter([sprintf('!%s', $b24SourceProp) => null]); + $elementsWithoutFilledSourceProp = $service->countByFilter([sprintf('%s', $b24SourceProp) => null]); + $elementsWithFilledDestinationProp = $service->countByFilter([sprintf('!%s', $b24DestinationProp) => null]); + $elementsWithoutFilledDestinationProp = $service->countByFilter([sprintf('%s', $b24DestinationProp) => null]); + + $io->info( + [ + '', + sprintf('total elements count: %s ', $totalElementsCnt), + sprintf('elements with filled source property: %s ', $elementsWithFilledSourceProp), + sprintf('elements without filled source property: %s ', $elementsWithoutFilledSourceProp), + sprintf('elements with filled destination property: %s ', $elementsWithFilledDestinationProp), + sprintf('elements without filled destination property: %s ', $elementsWithoutFilledDestinationProp) + ] + ); + $io->info('read data from bitrix24...'); + // read data from source and destinations properties + $dataFromProperties = $this->readDataFromProperties($output, $service, [ + sprintf('!%s', $b24SourceProp) => '' + ], $b24SourceProp, $b24DestinationProp); + + // exclude items with filled destination property + $dataToCopy = []; + $conflictData = []; + foreach ($dataFromProperties as $id => $item) { + // pass items with copied values + if ($item[$b24SourceProp] === $item[$b24DestinationProp]) { + continue; + } + + // filter conflict items + if($item[$b24DestinationProp] !== '') { + $conflictData[$id] = $item; + } + // filter items to copy + if($item[$b24DestinationProp] === '') { + $dataToCopy[$id] = $item; + } + } + $io->newLine(); + $io->warning([ + '', + sprintf('conflict items count: %s', count($conflictData)), + sprintf('problem id: %s', implode(', ', array_keys($conflictData))), + ]); + + $io->info([ + '', + sprintf('items to copy count: %s', count($dataToCopy)) + ]); + + // build update command + $updateCmd = $this->createUpdateCommand($dataToCopy, $b24SourceProp, $b24DestinationProp); + + // update items + $this->updateItems($output, $service, $updateCmd); + + $io->success('all items updated'); + } catch (BaseException $exception) { + $io = new SymfonyStyle($input, $output); + $io->caution(sprintf('error message: %s', $exception->getMessage())); + $io->text( + $exception->getTraceAsString() + ); + } catch (\Throwable $exception) { + $io = new SymfonyStyle($input, $output); + $io->caution('unknown error'); + $io->text( + [ + sprintf('%s', $exception->getMessage()), + ] + ); + } + $this->logger->debug('CopyPropertyValues.finish'); + + return Command::SUCCESS; + } +} \ No newline at end of file From 8f144c9aa30b8ac8f7f4b60b0dde81563b80448a Mon Sep 17 00:00:00 2001 From: mesilov Date: Tue, 22 Aug 2023 01:52:54 +0400 Subject: [PATCH 66/94] fix operating error on boxes Signed-off-by: mesilov --- src/Core/Response/DTO/Time.php | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/Core/Response/DTO/Time.php b/src/Core/Response/DTO/Time.php index e1302aa1..3e83f84b 100644 --- a/src/Core/Response/DTO/Time.php +++ b/src/Core/Response/DTO/Time.php @@ -33,25 +33,26 @@ class Time /** * Time constructor. * - * @param float $start - * @param float $finish - * @param float $duration - * @param float $processing - * @param float $operating + * @param float $start + * @param float $finish + * @param float $duration + * @param float $processing + * @param float $operating * @param \DateTimeImmutable $dateStart * @param \DateTimeImmutable $dateFinish - * @param int|null $operatingResetAt + * @param int|null $operatingResetAt */ public function __construct( - float $start, - float $finish, - float $duration, - float $processing, - float $operating, + float $start, + float $finish, + float $duration, + float $processing, + float $operating, DateTimeImmutable $dateStart, DateTimeImmutable $dateFinish, - ?int $operatingResetAt - ) { + ?int $operatingResetAt + ) + { $this->start = $start; $this->finish = $finish; $this->duration = $duration; @@ -139,7 +140,7 @@ public static function initFromResponse(array $response): self (float)$response['finish'], (float)$response['duration'], (float)$response['processing'], - (float)$response['operating'], + array_key_exists('operating', $response) ? (float)$response['operating'] : 0, new DateTimeImmutable($response['date_start']), new DateTimeImmutable($response['date_finish']), $response['operating_reset_at'] ?? null From e13f9214a6601d45a5feb1ef9b6d75cd0a8d51dd Mon Sep 17 00:00:00 2001 From: mesilov Date: Tue, 22 Aug 2023 01:54:29 +0400 Subject: [PATCH 67/94] fix errors on items tests Signed-off-by: mesilov --- src/Core/Batch.php | 2 +- src/Core/CoreBuilder.php | 23 ++++++------------- .../CRM/Common/Result/AbstractCrmItem.php | 4 +++- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/Core/Batch.php b/src/Core/Batch.php index 4c30f8c7..e647b904 100644 --- a/src/Core/Batch.php +++ b/src/Core/Batch.php @@ -210,7 +210,7 @@ public function updateEntityItems(string $apiMethod, array $entityItems): Genera $this->registerCommand($apiMethod, [ 'id' => $entityItemId, 'fields' => $entityItem['fields'], - 'params' => $entityItem['params'], + 'params' => $entityItem['params'] ?? null, ]); } diff --git a/src/Core/CoreBuilder.php b/src/Core/CoreBuilder.php index e188ae62..8e5b99d8 100644 --- a/src/Core/CoreBuilder.php +++ b/src/Core/CoreBuilder.php @@ -60,11 +60,6 @@ public function withCredentials(Credentials $credentials): self return $this; } - /** - * @param ApiClientInterface $apiClient - * - * @return $this - */ public function withApiClient(ApiClientInterface $apiClient): self { $this->apiClient = $apiClient; @@ -72,11 +67,13 @@ public function withApiClient(ApiClientInterface $apiClient): self return $this; } - /** - * @param LoggerInterface $logger - * - * @return $this - */ + public function withHttpClient(HttpClientInterface $httpClient):self + { + $this->httpClient = $httpClient; + + return $this; + } + public function withLogger(LoggerInterface $logger): self { $this->logger = $logger; @@ -84,11 +81,6 @@ public function withLogger(LoggerInterface $logger): self return $this; } - /** - * @param EventDispatcherInterface $eventDispatcher - * - * @return $this - */ public function withEventDispatcher(EventDispatcherInterface $eventDispatcher): self { $this->eventDispatcher = $eventDispatcher; @@ -97,7 +89,6 @@ public function withEventDispatcher(EventDispatcherInterface $eventDispatcher): } /** - * @return CoreInterface * @throws InvalidArgumentException */ public function build(): CoreInterface diff --git a/src/Services/CRM/Common/Result/AbstractCrmItem.php b/src/Services/CRM/Common/Result/AbstractCrmItem.php index 81467927..b8670f54 100644 --- a/src/Services/CRM/Common/Result/AbstractCrmItem.php +++ b/src/Services/CRM/Common/Result/AbstractCrmItem.php @@ -142,7 +142,9 @@ public function __get($offset) */ protected function getKeyWithUserfieldByFieldName(string $fieldName) { - $fieldName = self::CRM_USERFIELD_PREFIX . $fieldName; + if(!str_starts_with($fieldName, self::CRM_USERFIELD_PREFIX)) { + $fieldName = self::CRM_USERFIELD_PREFIX . $fieldName; + } if (!$this->isKeyExists($fieldName)) { throw new UserfieldNotFoundException(sprintf('crm userfield not found by field name %s', $fieldName)); } From ccf72f1228c672e9404fdbbf4abed0d2432a3e75 Mon Sep 17 00:00:00 2001 From: mesilov Date: Thu, 24 Aug 2023 01:46:27 +0400 Subject: [PATCH 68/94] change interface Bitrix24AccountInterface signature Signed-off-by: mesilov --- CHANGELOG.md | 10 +++ .../Bitrix24AccountInterface.php | 67 ++++++++++++++----- 2 files changed, 62 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40b2ac1b..d2284a5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,16 @@ to `Bitrix24\SDK\Services\Telephony\Requests\Events\OnExternalCallStart\OnExternalCallStart` * from `Bitrix24\SDK\Services\Telephony\Requests\Events\OnVoximplantCallEnd` to `Bitrix24\SDK\Services\Telephony\Requests\Events\OnVoximplantCallEnd\OnVoximplantCallEnd` +* ❗Changes in `Bitrix24\SDK\Application\Contracts\Bitrix24Account\Bitrix24AccountInterface`: + * method `getContactPerson` renamed to `getContactPersonId` + * added method `getApplicationVersion` + * added method `updateApplicationVersion` + * added method `getApplicationScope` + * added method `applicationInstalled` + * added method `applicationUninstalled` + * added method `markAccountAsDeactivated` + * removed method `markAccountAsDeleted` + * changed method `markAccountAsActive` signature ### Bugfix diff --git a/src/Application/Contracts/Bitrix24Account/Bitrix24AccountInterface.php b/src/Application/Contracts/Bitrix24Account/Bitrix24AccountInterface.php index dcde72d7..f57e0f58 100644 --- a/src/Application/Contracts/Bitrix24Account/Bitrix24AccountInterface.php +++ b/src/Application/Contracts/Bitrix24Account/Bitrix24AccountInterface.php @@ -4,69 +4,106 @@ namespace Bitrix24\SDK\Application\Contracts\Bitrix24Account; +use Bitrix24\SDK\Core\Credentials\Scope; use Symfony\Component\Uid\Uuid; use Bitrix24\SDK\Core\Response\DTO\RenewedAccessToken; interface Bitrix24AccountInterface { /** - * @return \Symfony\Component\Uid\Uuid + * @return Uuid */ public function getId(): Uuid; + /** - * @return \Symfony\Component\Uid\Uuid + * @return Uuid */ - public function getContactPerson(): Uuid; + public function getContactPersonId(): Uuid; + /** * @return string */ public function getMemberId(): string; + /** * @return string */ public function getDomainUrl(): string; + /** * @return Bitrix24AccountStatus */ public function getStatus(): Bitrix24AccountStatus; + /** * @return string */ public function getAccessToken(): string; + /** * @return string */ public function getRefreshToken(): string; + /** * @return int */ public function getExpires(): int; + /** - * @param \Bitrix24\SDK\Core\Response\DTO\RenewedAccessToken $renewedAccessToken + * Get application version * - * @return void + * @return int */ - public function renewAccessToken(RenewedAccessToken $renewedAccessToken): void; + public function getApplicationVersion(): int; + /** - * @param string $newDomainUrl + * Update application version if application was updated in marketplace * + * @param int $version + * @param Scope|null $newScope * @return void */ - public function changeDomainUrl(string $newDomainUrl): void; + public function updateApplicationVersion(int $version, ?Scope $newScope): void; + /** - * Switch account to Active status - installation is finalized + * Get application scope (permissions) * - * @param string $applicationToken + * @return Scope + */ + public function getApplicationScope(): Scope; + + /** + * @param RenewedAccessToken $renewedAccessToken * * @return void */ - public function markAccountAsActive(string $applicationToken): void; + public function renewAccessToken(RenewedAccessToken $renewedAccessToken): void; + /** - * Change account status to Deleted - * - * @param string $applicationToken + * @param string $newDomainUrl * * @return void */ - public function markAccountAsDeleted(string $applicationToken): void; + public function changeDomainUrl(string $newDomainUrl): void; + + /** + * Application installed on portal and finish installation flow + */ + public function applicationInstalled(string $applicationToken): void; + + /** + * Application uninstalled from portal + */ + public function applicationUninstalled(string $applicationToken): void; + + /** + * Switch account to Active status + */ + public function markAccountAsActive(): void; + + /** + * Change account status to Deactivated + */ + public function markAccountAsDeactivated(): void; } \ No newline at end of file From b2349539411828209c9d4412d9aec9673727aa37 Mon Sep 17 00:00:00 2001 From: mesilov Date: Sun, 27 Aug 2023 00:49:04 +0400 Subject: [PATCH 69/94] add bitrix24 user id Signed-off-by: mesilov --- CHANGELOG.md | 1 + .../Contracts/Bitrix24Account/Bitrix24AccountInterface.php | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2284a5f..4f62f651 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ * added method `applicationInstalled` * added method `applicationUninstalled` * added method `markAccountAsDeactivated` + * added method `getBitrix24UserId` * removed method `markAccountAsDeleted` * changed method `markAccountAsActive` signature diff --git a/src/Application/Contracts/Bitrix24Account/Bitrix24AccountInterface.php b/src/Application/Contracts/Bitrix24Account/Bitrix24AccountInterface.php index f57e0f58..6c5109a1 100644 --- a/src/Application/Contracts/Bitrix24Account/Bitrix24AccountInterface.php +++ b/src/Application/Contracts/Bitrix24Account/Bitrix24AccountInterface.php @@ -106,4 +106,9 @@ public function markAccountAsActive(): void; * Change account status to Deactivated */ public function markAccountAsDeactivated(): void; + + /** + * Get Bitrix24 user id who installed application and own this account + */ + public function getBitrix24UserId(): int; } \ No newline at end of file From d2a8a89fdc3846f57316d8dcb939f59fbdfd6e57 Mon Sep 17 00:00:00 2001 From: mesilov Date: Mon, 28 Aug 2023 21:55:54 +0400 Subject: [PATCH 70/94] change signatures in application contracts Signed-off-by: mesilov --- CHANGELOG.md | 13 ++++- .../Bitrix24AccountInterface.php | 4 +- .../Bitrix24AccountRepositoryInterface.php | 50 ++++++------------- 3 files changed, 29 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f62f651..ef1eab2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,10 +41,19 @@ * added method `getApplicationScope` * added method `applicationInstalled` * added method `applicationUninstalled` - * added method `markAccountAsDeactivated` + * added method `markAsDeactivated` * added method `getBitrix24UserId` * removed method `markAccountAsDeleted` - * changed method `markAccountAsActive` signature + * changed method `markAsActive` +* ❗Changes in `Bitrix24\SDK\Application\Contracts\Bitrix24Account\Bitrix24AccountRepositoryInterface`: + * method `saveAccount` renamed to `save` + * method `deleteAccount` renamed to `delete` + * method `findAccountByMemberId` renamed to `findByMemberId` + * method `getAccountByMemberId` renamed to `getByMemberId` + * method `findAccountByContactPersonId` renamed to `findByContactPersonId` + * method `findAccountByDomainUrl` renamed to `findByDomainUrl` + * add method `findAllActive` + * add method `findAllDeactivated` ### Bugfix diff --git a/src/Application/Contracts/Bitrix24Account/Bitrix24AccountInterface.php b/src/Application/Contracts/Bitrix24Account/Bitrix24AccountInterface.php index 6c5109a1..654f377b 100644 --- a/src/Application/Contracts/Bitrix24Account/Bitrix24AccountInterface.php +++ b/src/Application/Contracts/Bitrix24Account/Bitrix24AccountInterface.php @@ -100,12 +100,12 @@ public function applicationUninstalled(string $applicationToken): void; /** * Switch account to Active status */ - public function markAccountAsActive(): void; + public function markAsActive(): void; /** * Change account status to Deactivated */ - public function markAccountAsDeactivated(): void; + public function markAsDeactivated(): void; /** * Get Bitrix24 user id who installed application and own this account diff --git a/src/Application/Contracts/Bitrix24Account/Bitrix24AccountRepositoryInterface.php b/src/Application/Contracts/Bitrix24Account/Bitrix24AccountRepositoryInterface.php index deeae92e..980b7e3a 100644 --- a/src/Application/Contracts/Bitrix24Account/Bitrix24AccountRepositoryInterface.php +++ b/src/Application/Contracts/Bitrix24Account/Bitrix24AccountRepositoryInterface.php @@ -10,64 +10,46 @@ interface Bitrix24AccountRepositoryInterface { /** * Save account - * - * @param Bitrix24AccountInterface $entity - * @param bool $flush - * - * @return void */ - public function saveAccount(Bitrix24AccountInterface $entity, bool $flush = false): void; + public function save(Bitrix24AccountInterface $entity, bool $flush = false): void; /** * Get by account id - * - * @param \Symfony\Component\Uid\Uuid $id - * - * @return Bitrix24AccountInterface */ public function getById(Uuid $id): Bitrix24AccountInterface; /** * Delete account - * - * @param Bitrix24AccountInterface $entity - * @param bool $flush - * - * @return void */ - public function deleteAccount(Bitrix24AccountInterface $entity, bool $flush = false): void; + public function delete(Bitrix24AccountInterface $entity, bool $flush = false): void; /** * Find account by member_id - * - * @return ?Bitrix24AccountInterface Returns an array of Bitrix24Account objects */ - public function findAccountByMemberId(string $memberId): ?Bitrix24AccountInterface; + public function findByMemberId(string $memberId): ?Bitrix24AccountInterface; /** * Get account by member_id - * - * @param string $memberId - * - * @return Bitrix24AccountInterface */ - public function getAccountByMemberId(string $memberId): Bitrix24AccountInterface; + public function getByMemberId(string $memberId): Bitrix24AccountInterface; /** * Find account by contact person id - person, who installed application - * - * @param \Symfony\Component\Uid\Uuid $contactPersonId - * - * @return Bitrix24AccountInterface|null */ - public function findAccountByContactPersonId(Uuid $contactPersonId): ?Bitrix24AccountInterface; + public function findByContactPersonId(Uuid $contactPersonId): ?Bitrix24AccountInterface; /** * Find account by domain url - * - * @param string $domainUrl - * - * @return Bitrix24AccountInterface|null */ - public function findAccountByDomainUrl(string $domainUrl): ?Bitrix24AccountInterface; + public function findByDomainUrl(string $domainUrl): ?Bitrix24AccountInterface; + + /** + * @return array + */ + public function findAllActive(): array; + + /** + * @return array + */ + public function findAllDeactivated(): array; } \ No newline at end of file From 6884661e51d9268cd7c38e7a123dbc12d88559e7 Mon Sep 17 00:00:00 2001 From: mesilov Date: Thu, 7 Sep 2023 02:27:06 +0400 Subject: [PATCH 71/94] add deal stage semantic id Signed-off-by: mesilov --- CHANGELOG.md | 1 + src/Services/CRM/Deal/DealStageSemanticId.php | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 src/Services/CRM/Deal/DealStageSemanticId.php diff --git a/CHANGELOG.md b/CHANGELOG.md index ef1eab2b..39555077 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * `Bitrix24\SDK\Services\User\Service\User::update` - update user * `Bitrix24\SDK\Services\User\Service\User::search` - search users * add [crm item support](https://github.com/mesilov/bitrix24-php-sdk/issues/330) +* add enum `DealStageSemanticId` * add Duplicate search support for `Bitrix24\SDK\Services\CRM\Duplicates\Service\Duplicate` ### Changed diff --git a/src/Services/CRM/Deal/DealStageSemanticId.php b/src/Services/CRM/Deal/DealStageSemanticId.php new file mode 100644 index 00000000..e591b956 --- /dev/null +++ b/src/Services/CRM/Deal/DealStageSemanticId.php @@ -0,0 +1,18 @@ + Date: Sat, 9 Sep 2023 02:03:05 +0400 Subject: [PATCH 72/94] fix errors for scope Signed-off-by: mesilov --- CHANGELOG.md | 1 + src/Core/Credentials/Scope.php | 16 +++++------ .../Credentials/ApplicationProfileTest.php | 28 ++++++++++++------- tests/Unit/Core/Credentials/ScopeTest.php | 9 ++++++ 4 files changed, 36 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39555077..a8138f8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,7 @@ * fix [add helper metods isError for registerCallResult fortelephony](https://github.com/mesilov/bitrix24-php-sdk/issues/335) * fix [add return type for crm multifields phone, email, im](https://github.com/mesilov/bitrix24-php-sdk/issues/338) * fix errors in `ShowFieldsDescriptionCommand` metadata reader CLI command +* fix errors for `ApplicationProfile` with empty scope ### etc * move CLI entry point to `bin/console` diff --git a/src/Core/Credentials/Scope.php b/src/Core/Credentials/Scope.php index 6e64d1aa..d028e1e7 100644 --- a/src/Core/Credentials/Scope.php +++ b/src/Core/Credentials/Scope.php @@ -6,11 +6,6 @@ use Bitrix24\SDK\Core\Exceptions\UnknownScopeCodeException; -/** - * Class Scope - * - * @package Bitrix24\SDK\Core\Credentials - */ class Scope { /** @@ -84,9 +79,14 @@ class Scope public function __construct(array $scope = []) { $scope = array_unique(array_map('strtolower', $scope)); - foreach ($scope as $item) { - if (!in_array($item, $this->availableScope, true)) { - throw new UnknownScopeCodeException(sprintf('unknown application scope code - %s', $item)); + + if (count($scope) === 1 && $scope[0] === '') { + $scope = []; + } else { + foreach ($scope as $item) { + if (!in_array($item, $this->availableScope, true)) { + throw new UnknownScopeCodeException(sprintf('unknown application scope code - %s', $item)); + } } } diff --git a/tests/Unit/Core/Credentials/ApplicationProfileTest.php b/tests/Unit/Core/Credentials/ApplicationProfileTest.php index a66a38ce..17956aac 100644 --- a/tests/Unit/Core/Credentials/ApplicationProfileTest.php +++ b/tests/Unit/Core/Credentials/ApplicationProfileTest.php @@ -13,11 +13,11 @@ class ApplicationProfileTest extends TestCase { /** * - * @param array $arr + * @param array $arr * @param string|null $expectedException * * @return void - * @throws \Bitrix24\SDK\Core\Exceptions\InvalidArgumentException + * @throws InvalidArgumentException * @dataProvider arrayDataProvider */ public function testFromArray(array $arr, ?string $expectedException): void @@ -35,35 +35,43 @@ public function arrayDataProvider(): Generator { yield 'valid' => [ [ - 'BITRIX24_PHP_SDK_APPLICATION_CLIENT_ID' => '1', + 'BITRIX24_PHP_SDK_APPLICATION_CLIENT_ID' => '1', 'BITRIX24_PHP_SDK_APPLICATION_CLIENT_SECRET' => '2', - 'BITRIX24_PHP_SDK_APPLICATION_SCOPE' => 'user', + 'BITRIX24_PHP_SDK_APPLICATION_SCOPE' => 'user', ], null, ]; yield 'without client id' => [ [ - '' => '1', + '' => '1', 'BITRIX24_PHP_SDK_APPLICATION_CLIENT_SECRET' => '2', - 'BITRIX24_PHP_SDK_APPLICATION_SCOPE' => 'user', + 'BITRIX24_PHP_SDK_APPLICATION_SCOPE' => 'user', ], InvalidArgumentException::class, ]; yield 'without client secret' => [ [ 'BITRIX24_PHP_SDK_APPLICATION_CLIENT_ID' => '1', - '' => '2', - 'BITRIX24_PHP_SDK_APPLICATION_SCOPE' => 'user', + '' => '2', + 'BITRIX24_PHP_SDK_APPLICATION_SCOPE' => 'user', ], InvalidArgumentException::class, ]; yield 'without client application scope' => [ [ - 'BITRIX24_PHP_SDK_APPLICATION_CLIENT_ID' => '1', + 'BITRIX24_PHP_SDK_APPLICATION_CLIENT_ID' => '1', 'BITRIX24_PHP_SDK_APPLICATION_CLIENT_SECRET' => '2', - '' => 'user', + '' => 'user', ], InvalidArgumentException::class, ]; + yield 'with empty scope' => [ + [ + 'BITRIX24_PHP_SDK_APPLICATION_CLIENT_ID' => '1', + 'BITRIX24_PHP_SDK_APPLICATION_CLIENT_SECRET' => '2', + 'BITRIX24_PHP_SDK_APPLICATION_SCOPE' => '', + ], + null + ]; } } diff --git a/tests/Unit/Core/Credentials/ScopeTest.php b/tests/Unit/Core/Credentials/ScopeTest.php index d7d37b32..e974fd36 100644 --- a/tests/Unit/Core/Credentials/ScopeTest.php +++ b/tests/Unit/Core/Credentials/ScopeTest.php @@ -73,6 +73,15 @@ public function testUnknownScope(): void $scope = new Scope(['fooo']); } + /** + * @throws UnknownScopeCodeException + */ + public function testEmptyScope(): void + { + $scope = new Scope(['']); + $this->assertEquals([], $scope->getScopeCodes()); + } + /** * @throws UnknownScopeCodeException */ From eb1f34a7b0b6b8f3eff4d0aea503a69284a24232 Mon Sep 17 00:00:00 2001 From: mesilov Date: Sat, 9 Sep 2023 02:34:53 +0400 Subject: [PATCH 73/94] fix errors for Core Signed-off-by: mesilov --- CHANGELOG.md | 3 ++- src/Core/Core.php | 10 ++++++++++ .../Exceptions/AuthForbiddenException.php | 9 +++++++++ tests/Integration/Core/CoreTest.php | 19 +++++++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 src/Core/Exceptions/AuthForbiddenException.php diff --git a/CHANGELOG.md b/CHANGELOG.md index a8138f8d..81ff9ad6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,7 +64,8 @@ * fix [add helper metods isError for registerCallResult fortelephony](https://github.com/mesilov/bitrix24-php-sdk/issues/335) * fix [add return type for crm multifields phone, email, im](https://github.com/mesilov/bitrix24-php-sdk/issues/338) * fix errors in `ShowFieldsDescriptionCommand` metadata reader CLI command -* fix errors for `ApplicationProfile` with empty scope +* fix errors for `ApplicationProfile` with empty scope +* fix errors in `Core` with auth attempt to non-exists portal ### etc * move CLI entry point to `bin/console` diff --git a/src/Core/Core.php b/src/Core/Core.php index a739fe9a..73511395 100644 --- a/src/Core/Core.php +++ b/src/Core/Core.php @@ -7,6 +7,7 @@ use Bitrix24\SDK\Core\Commands\Command; use Bitrix24\SDK\Core\Contracts\ApiClientInterface; use Bitrix24\SDK\Core\Contracts\CoreInterface; +use Bitrix24\SDK\Core\Exceptions\AuthForbiddenException; use Bitrix24\SDK\Core\Exceptions\BaseException; use Bitrix24\SDK\Core\Exceptions\TransportException; use Bitrix24\SDK\Core\Response\Response; @@ -146,6 +147,15 @@ public function call(string $apiMethod, array $parameters = []): Response throw new BaseException('UNAUTHORIZED request error'); } break; + case StatusCodeInterface::STATUS_FORBIDDEN: + $this->logger->warning( + 'bitrix24 portal authorisation forbidden', + [ + 'apiMethod' => $apiMethod, + 'b24DomainUrl' => $this->apiClient->getCredentials()->getDomainUrl(), + ] + ); + throw new AuthForbiddenException(sprintf('authorisation forbidden for portal %s ', $this->apiClient->getCredentials()->getDomainUrl())); case StatusCodeInterface::STATUS_SERVICE_UNAVAILABLE: $body = $apiCallResponse->toArray(false); $this->logger->notice( diff --git a/src/Core/Exceptions/AuthForbiddenException.php b/src/Core/Exceptions/AuthForbiddenException.php new file mode 100644 index 00000000..2d444876 --- /dev/null +++ b/src/Core/Exceptions/AuthForbiddenException.php @@ -0,0 +1,9 @@ +assertIsArray($response->getResponseData()->getResult()); } + public function testConnectToNonExistsBitrix24PortalInCloud():void + { + $core = (new CoreBuilder()) + ->withCredentials(Credentials::createFromOAuth( + new AccessToken('non-exists-access-token','refresh-token', 3600), + new ApplicationProfile('non-exists-client-id', 'non-exists-client-secret', new Scope([])), + 'non-exists-domain.bitrix24.com' + )) + ->build(); + $this->expectException(AuthForbiddenException::class); + $core->call('app.info'); + } + /** * @return void * @throws \Bitrix24\SDK\Core\Exceptions\BaseException From 50cf106d811a2746c37b988b0727253707141a43 Mon Sep 17 00:00:00 2001 From: mesilov Date: Sat, 25 Nov 2023 01:36:35 +0600 Subject: [PATCH 74/94] add new line in changelog Signed-off-by: mesilov --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81ff9ad6..2c93216c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ * add [crm item support](https://github.com/mesilov/bitrix24-php-sdk/issues/330) * add enum `DealStageSemanticId` * add Duplicate search support for `Bitrix24\SDK\Services\CRM\Duplicates\Service\Duplicate` +* add `x-request-id` [header support](https://github.com/mesilov/bitrix24-php-sdk/issues/354) ### Changed From 352e46d4535ab8305a9616faf446d3c3a1e104f3 Mon Sep 17 00:00:00 2001 From: mesilov Date: Sat, 25 Nov 2023 11:32:56 +0600 Subject: [PATCH 75/94] add request id generator proto Signed-off-by: mesilov --- src/Core/ApiClient.php | 46 +++++++++++++++---- src/Core/CoreBuilder.php | 22 ++++++--- .../RequestId/DefaultRequestIdGenerator.php | 22 +++++++++ .../RequestId/RequestIdGeneratorInterface.php | 12 +++++ tests/Unit/Stubs/NullCore.php | 12 ++--- 5 files changed, 93 insertions(+), 21 deletions(-) create mode 100644 src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php create mode 100644 src/Infrastructure/HttpClient/RequestId/RequestIdGeneratorInterface.php diff --git a/src/Core/ApiClient.php b/src/Core/ApiClient.php index ac7387e0..9f2f6125 100644 --- a/src/Core/ApiClient.php +++ b/src/Core/ApiClient.php @@ -5,9 +5,11 @@ namespace Bitrix24\SDK\Core; use Bitrix24\SDK\Core\Contracts\ApiClientInterface; +use Bitrix24\SDK\Core\Credentials\Credentials; use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; use Bitrix24\SDK\Core\Exceptions\TransportException; use Bitrix24\SDK\Core\Response\DTO\RenewedAccessToken; +use Bitrix24\SDK\Infrastructure\HttpClient\RequestId\RequestIdGeneratorInterface; use Fig\Http\Message\StatusCodeInterface; use Psr\Log\LoggerInterface; use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; @@ -18,7 +20,8 @@ class ApiClient implements ApiClientInterface { protected HttpClientInterface $client; protected LoggerInterface $logger; - protected Credentials\Credentials $credentials; + protected Credentials $credentials; + protected RequestIdGeneratorInterface $requestIdGenerator; /** * @const string */ @@ -32,14 +35,20 @@ class ApiClient implements ApiClientInterface /** * ApiClient constructor. * - * @param Credentials\Credentials $credentials + * @param Credentials $credentials * @param HttpClientInterface $client + * @param RequestIdGeneratorInterface $requestIdGenerator * @param LoggerInterface $logger */ - public function __construct(Credentials\Credentials $credentials, HttpClientInterface $client, LoggerInterface $logger) + public function __construct( + Credentials $credentials, + HttpClientInterface $client, + RequestIdGeneratorInterface $requestIdGenerator, + LoggerInterface $logger) { $this->credentials = $credentials; $this->client = $client; + $this->requestIdGenerator = $requestIdGenerator; $this->logger = $logger; $this->logger->debug( 'ApiClient.init', @@ -64,9 +73,9 @@ protected function getDefaultHeaders(): array } /** - * @return Credentials\Credentials + * @return Credentials */ - public function getCredentials(): Credentials\Credentials + public function getCredentials(): Credentials { return $this->credentials; } @@ -80,7 +89,10 @@ public function getCredentials(): Credentials\Credentials */ public function getNewAccessToken(): RenewedAccessToken { - $this->logger->debug('getNewAccessToken.start'); + $requestId = $this->requestIdGenerator->getRequestId(); + $this->logger->debug('getNewAccessToken.start', [ + 'requestId' => $requestId + ]); if ($this->getCredentials()->getApplicationProfile() === null) { throw new InvalidArgumentException('application profile not set'); } @@ -103,14 +115,21 @@ public function getNewAccessToken(): RenewedAccessToken ); $requestOptions = [ - 'headers' => $this->getDefaultHeaders(), + 'headers' => array_merge( + $this->getDefaultHeaders(), + [ + $this->requestIdGenerator->getHeaderFieldName() => $requestId + ] + ), ]; $response = $this->client->request($method, $url, $requestOptions); $responseData = $response->toArray(false); if ($response->getStatusCode() === StatusCodeInterface::STATUS_OK) { $newAccessToken = RenewedAccessToken::initFromArray($responseData); - $this->logger->debug('getNewAccessToken.finish'); + $this->logger->debug('getNewAccessToken.finish', [ + 'requestId' => $requestId + ]); return $newAccessToken; } if ($response->getStatusCode() === StatusCodeInterface::STATUS_BAD_REQUEST) { @@ -129,12 +148,14 @@ public function getNewAccessToken(): RenewedAccessToken */ public function getResponse(string $apiMethod, array $parameters = []): ResponseInterface { + $requestId = $this->requestIdGenerator->getRequestId(); $this->logger->info( 'getResponse.start', [ 'apiMethod' => $apiMethod, 'domainUrl' => $this->credentials->getDomainUrl(), 'parameters' => $parameters, + 'requestId' => $requestId ] ); @@ -150,9 +171,15 @@ public function getResponse(string $apiMethod, array $parameters = []): Response $parameters['auth'] = $this->getCredentials()->getAccessToken()->getAccessToken(); } + $requestOptions = [ 'json' => $parameters, - 'headers' => $this->getDefaultHeaders(), + 'headers' => array_merge( + $this->getDefaultHeaders(), + [ + $this->requestIdGenerator->getHeaderFieldName() => $requestId + ] + ), // disable redirects, try to catch portal change domain name event 'max_redirects' => 0, ]; @@ -163,6 +190,7 @@ public function getResponse(string $apiMethod, array $parameters = []): Response [ 'apiMethod' => $apiMethod, 'responseInfo' => $response->getInfo(), + 'requestId' => $requestId ] ); diff --git a/src/Core/CoreBuilder.php b/src/Core/CoreBuilder.php index 8e5b99d8..8f0d7960 100644 --- a/src/Core/CoreBuilder.php +++ b/src/Core/CoreBuilder.php @@ -9,6 +9,8 @@ use Bitrix24\SDK\Core\Credentials\Credentials; use Bitrix24\SDK\Core\Credentials\WebhookUrl; use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Infrastructure\HttpClient\RequestId\DefaultRequestIdGenerator; +use Bitrix24\SDK\Infrastructure\HttpClient\RequestId\RequestIdGeneratorInterface; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; use Symfony\Component\EventDispatcher\EventDispatcher; @@ -23,12 +25,13 @@ */ class CoreBuilder { - protected ?ApiClientInterface $apiClient; - protected HttpClientInterface $httpClient; - protected EventDispatcherInterface $eventDispatcher; - protected LoggerInterface $logger; - protected ?Credentials $credentials; - protected ApiLevelErrorHandler $apiLevelErrorHandler; + private ?ApiClientInterface $apiClient; + private HttpClientInterface $httpClient; + private EventDispatcherInterface $eventDispatcher; + private LoggerInterface $logger; + private ?Credentials $credentials; + private ApiLevelErrorHandler $apiLevelErrorHandler; + private RequestIdGeneratorInterface $requestIdGenerator; /** * CoreBuilder constructor. @@ -46,6 +49,12 @@ public function __construct() $this->credentials = null; $this->apiClient = null; $this->apiLevelErrorHandler = new ApiLevelErrorHandler($this->logger); + $this->requestIdGenerator = new DefaultRequestIdGenerator(); + } + + public function withRequestIdGenerator(RequestIdGeneratorInterface $requestIdGenerator): void + { + $this->requestIdGenerator = $requestIdGenerator; } /** @@ -101,6 +110,7 @@ public function build(): CoreInterface $this->apiClient = new ApiClient( $this->credentials, $this->httpClient, + $this->requestIdGenerator, $this->logger ); } diff --git a/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php b/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php new file mode 100644 index 00000000..05fba394 --- /dev/null +++ b/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php @@ -0,0 +1,22 @@ +toRfc4122(); + } + + public function getHeaderFieldName(): string + { + return 'X-Request-ID'; + } +} \ No newline at end of file diff --git a/src/Infrastructure/HttpClient/RequestId/RequestIdGeneratorInterface.php b/src/Infrastructure/HttpClient/RequestId/RequestIdGeneratorInterface.php new file mode 100644 index 00000000..60dd9f0f --- /dev/null +++ b/src/Infrastructure/HttpClient/RequestId/RequestIdGeneratorInterface.php @@ -0,0 +1,12 @@ + Date: Sun, 26 Nov 2023 02:23:22 +0600 Subject: [PATCH 76/94] first version Signed-off-by: mesilov --- .../RequestId/DefaultRequestIdGenerator.php | 36 +++++++++++++-- .../DefaultRequestIdGeneratorTest.php | 44 +++++++++++++++++++ 2 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 tests/Unit/Infrastructure/HttpClient/RequestId/DefaultRequestIdGeneratorTest.php diff --git a/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php b/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php index 05fba394..d0332e8b 100644 --- a/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php +++ b/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php @@ -8,15 +8,43 @@ class DefaultRequestIdGenerator implements RequestIdGeneratorInterface { - public function getRequestId(): string + private const DEFAULT_REQUEST_ID_FIELD_NAME = 'X-Request-ID'; + private const KEY_NAME_VARIANTS = [ + 'REQUEST_ID', + 'HTTP_X_REQUEST_ID', + 'UNIQUE_ID' + ]; + + private function generate(): string { - // get from server fields - // if empty - generate return Uuid::v7()->toRfc4122(); } + private function findExists(): ?string + { + $candidate = null; + foreach(self::KEY_NAME_VARIANTS as $key) + { + if(!empty($_SERVER[$key])) + { + $candidate = $_SERVER[$key]; + break; + } + } + return $candidate; + } + + public function getRequestId(): string + { + $reqId = $this->findExists(); + if ($reqId === null) { + $reqId = $this->generate(); + } + return $reqId; + } + public function getHeaderFieldName(): string { - return 'X-Request-ID'; + return self::DEFAULT_REQUEST_ID_FIELD_NAME; } } \ No newline at end of file diff --git a/tests/Unit/Infrastructure/HttpClient/RequestId/DefaultRequestIdGeneratorTest.php b/tests/Unit/Infrastructure/HttpClient/RequestId/DefaultRequestIdGeneratorTest.php new file mode 100644 index 00000000..fab333b8 --- /dev/null +++ b/tests/Unit/Infrastructure/HttpClient/RequestId/DefaultRequestIdGeneratorTest.php @@ -0,0 +1,44 @@ +assertEquals($requestId, $gen->getRequestId()); + unset($_SERVER[$requestIdKey]); + } + + public function requestIdKeyDataProvider(): Generator + { + yield 'REQUEST_ID' => [ + 'REQUEST_ID', + Uuid::v7()->toRfc4122() + ]; + yield 'HTTP_X_REQUEST_ID' => [ + 'HTTP_X_REQUEST_ID', + Uuid::v7()->toRfc4122() + ]; + yield 'UNIQUE_ID' => [ + 'UNIQUE_ID', + Uuid::v7()->toRfc4122() + ]; + } +} From 2a50cabc6baaa53957b4a71aaee34d35cb674041 Mon Sep 17 00:00:00 2001 From: mesilov Date: Sun, 10 Dec 2023 02:14:58 +0600 Subject: [PATCH 77/94] Add Request ID to query string parameters The Request ID parameter is now included in query strings in addition to the header field for improved tracking. This change was made to accommodate for the current version of the Bitrix24 API that does not use Request ID from headers. A corresponding `getQueryStringParameterName` method was also added to the `RequestIdGeneratorInterface`. Signed-off-by: mesilov --- src/Core/ApiClient.php | 7 +++--- .../RequestId/DefaultRequestIdGenerator.php | 23 +++++++++++-------- .../RequestId/RequestIdGeneratorInterface.php | 2 ++ 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/Core/ApiClient.php b/src/Core/ApiClient.php index 9f2f6125..821c1172 100644 --- a/src/Core/ApiClient.php +++ b/src/Core/ApiClient.php @@ -84,7 +84,6 @@ public function getCredentials(): Credentials * @return RenewedAccessToken * @throws InvalidArgumentException * @throws TransportExceptionInterface - * @throws \JsonException * @throws TransportException */ public function getNewAccessToken(): RenewedAccessToken @@ -110,6 +109,7 @@ public function getNewAccessToken(): RenewedAccessToken 'client_id' => $this->getCredentials()->getApplicationProfile()->getClientId(), 'client_secret' => $this->getCredentials()->getApplicationProfile()->getClientSecret(), 'refresh_token' => $this->getCredentials()->getAccessToken()->getRefreshToken(), + $this->requestIdGenerator->getQueryStringParameterName() => $requestId ] ) ); @@ -170,8 +170,9 @@ public function getResponse(string $apiMethod, array $parameters = []): Response } $parameters['auth'] = $this->getCredentials()->getAccessToken()->getAccessToken(); } - - + // duplicate request id in query string for current version of bitrix24 api + // vendor don't use request id from headers =( + $url .= '?' . $this->requestIdGenerator->getQueryStringParameterName() . '=' . $requestId; $requestOptions = [ 'json' => $parameters, 'headers' => array_merge( diff --git a/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php b/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php index d0332e8b..1fde5608 100644 --- a/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php +++ b/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php @@ -8,13 +8,20 @@ class DefaultRequestIdGenerator implements RequestIdGeneratorInterface { - private const DEFAULT_REQUEST_ID_FIELD_NAME = 'X-Request-ID'; + private const DEFAULT_REQUEST_ID_HEADER_FIELD_NAME = 'X-Request-ID'; + private const DEFAULT_QUERY_STRING_PARAMETER_NAME = 'request_id'; private const KEY_NAME_VARIANTS = [ 'REQUEST_ID', 'HTTP_X_REQUEST_ID', 'UNIQUE_ID' ]; + public function getQueryStringParameterName(): string + { + return self::DEFAULT_QUERY_STRING_PARAMETER_NAME; + } + + private function generate(): string { return Uuid::v7()->toRfc4122(); @@ -23,13 +30,11 @@ private function generate(): string private function findExists(): ?string { $candidate = null; - foreach(self::KEY_NAME_VARIANTS as $key) - { - if(!empty($_SERVER[$key])) - { - $candidate = $_SERVER[$key]; - break; - } + foreach (self::KEY_NAME_VARIANTS as $key) { + if (!empty($_SERVER[$key])) { + $candidate = $_SERVER[$key]; + break; + } } return $candidate; } @@ -45,6 +50,6 @@ public function getRequestId(): string public function getHeaderFieldName(): string { - return self::DEFAULT_REQUEST_ID_FIELD_NAME; + return self::DEFAULT_REQUEST_ID_HEADER_FIELD_NAME; } } \ No newline at end of file diff --git a/src/Infrastructure/HttpClient/RequestId/RequestIdGeneratorInterface.php b/src/Infrastructure/HttpClient/RequestId/RequestIdGeneratorInterface.php index 60dd9f0f..77ba06b2 100644 --- a/src/Infrastructure/HttpClient/RequestId/RequestIdGeneratorInterface.php +++ b/src/Infrastructure/HttpClient/RequestId/RequestIdGeneratorInterface.php @@ -9,4 +9,6 @@ interface RequestIdGeneratorInterface public function getRequestId(): string; public function getHeaderFieldName(): string; + + public function getQueryStringParameterName():string; } \ No newline at end of file From ea0bed3996113232239befd7fa25975ce180dcd7 Mon Sep 17 00:00:00 2001 From: mesilov Date: Thu, 4 Jan 2024 00:21:32 +0600 Subject: [PATCH 78/94] add windows platforms in unit-tests Signed-off-by: mesilov --- .github/workflows/phpunit.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index e1512640..713330af 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -11,14 +11,15 @@ jobs: tests: name: "PHPUnit tests" - runs-on: ubuntu-latest + runs-on: ${{ matrix.operating-system }} + strategy: matrix: php-version: - - "8.1" - - "8.2" + - "8.3" dependencies: [ highest ] + operating-system: [ ubuntu-latest, windows-latest ] steps: - name: "Checkout" From e220737930c0933c7e3a84427977b434575a673f Mon Sep 17 00:00:00 2001 From: mesilov Date: Thu, 4 Jan 2024 00:24:17 +0600 Subject: [PATCH 79/94] bump php version requirements Signed-off-by: mesilov --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index e2dc8095..084b2a9d 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ } ], "require": { - "php": "8.1.* || 8.2.*", + "php": "8.1.* || 8.2.* || 8.3.*", "ext-json": "*", "ext-bcmath": "*", "ext-curl": "*", From e52866c54866bea88677598002cc1980c98d740e Mon Sep 17 00:00:00 2001 From: mesilov Date: Thu, 4 Jan 2024 00:34:57 +0600 Subject: [PATCH 80/94] add extensions Signed-off-by: mesilov --- .github/workflows/phpunit.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 713330af..f64d078b 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -30,6 +30,7 @@ jobs: with: coverage: "none" php-version: "${{ matrix.php-version }}" + extensions: json, bcmath, curl - name: "Install dependencies" run: | From 2fb317b1a813691aa58ab830ce38bf1ece000ade Mon Sep 17 00:00:00 2001 From: mesilov Date: Thu, 4 Jan 2024 00:37:46 +0600 Subject: [PATCH 81/94] add extension intl Signed-off-by: mesilov --- .github/workflows/phpunit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index f64d078b..83c2d3c1 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -30,7 +30,7 @@ jobs: with: coverage: "none" php-version: "${{ matrix.php-version }}" - extensions: json, bcmath, curl + extensions: json, bcmath, curl, intl - name: "Install dependencies" run: | From 1a4278e98b4773e631ce92be8bdff47811aac7a0 Mon Sep 17 00:00:00 2001 From: mesilov Date: Thu, 4 Jan 2024 00:42:35 +0600 Subject: [PATCH 82/94] fix workflows Signed-off-by: mesilov --- .github/workflows/phpstan.yml | 5 ++--- .github/workflows/phpunit.yml | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 068e9864..70847366 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -13,8 +13,7 @@ jobs: fail-fast: false matrix: php-version: - - "8.1" - - "8.2" + - "8.3" dependencies: [ highest ] steps: @@ -26,7 +25,7 @@ jobs: with: coverage: "none" php-version: "${{ matrix.php-version }}" - extensions: mbstring + extensions: json, bcmath, curl, intl, mbstring tools: composer:v2 - name: "Install lowest dependencies" diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 83c2d3c1..f1244128 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -13,10 +13,11 @@ jobs: runs-on: ${{ matrix.operating-system }} - strategy: matrix: php-version: + - "8.1" + - "8.2" - "8.3" dependencies: [ highest ] operating-system: [ ubuntu-latest, windows-latest ] @@ -30,7 +31,7 @@ jobs: with: coverage: "none" php-version: "${{ matrix.php-version }}" - extensions: json, bcmath, curl, intl + extensions: json, bcmath, curl, intl, mbstring - name: "Install dependencies" run: | From 6a6ccef21406dba8778d730eb2d0e31cc06078d5 Mon Sep 17 00:00:00 2001 From: mesilov Date: Thu, 4 Jan 2024 10:22:15 +0600 Subject: [PATCH 83/94] bump php version to 8.3.* Signed-off-by: mesilov --- .github/workflows/phpunit.yml | 2 - composer.json | 43 +++++++++++----------- phpunit.xml.dist | 40 +++++++++----------- src/Services/CRM/Contact/Service/Batch.php | 29 ++------------- 4 files changed, 41 insertions(+), 73 deletions(-) diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index f1244128..14797cdc 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -16,8 +16,6 @@ jobs: strategy: matrix: php-version: - - "8.1" - - "8.2" - "8.3" dependencies: [ highest ] operating-system: [ ubuntu-latest, windows-latest ] diff --git a/composer.json b/composer.json index 084b2a9d..67e5a23e 100644 --- a/composer.json +++ b/composer.json @@ -12,38 +12,37 @@ "license": "MIT", "authors": [ { - "name": "Maxim Mesilov", + "name": "Maksim Mesilov", "homepage": "https://github.com/mesilov/" - }, - { - "name": "Kirill Hramov", - "homepage": "https://github.com/KarlsonComplete" } ], + "config": { + "sort-packages": true + }, "require": { - "php": "8.1.* || 8.2.* || 8.3.*", + "php": "8.3.*", "ext-json": "*", "ext-bcmath": "*", "ext-curl": "*", - "psr/log": "^1.1.4 || ^2.0 || ^3.0", + "ext-intl": "*", + "psr/log": "1.1.*", "fig/http-message-util": "1.1.*", - "symfony/http-client": "5.4.* || 6.*", - "symfony/http-client-contracts": "^2.5 || ^3.1", - "symfony/http-foundation": "5.4.* || 6.*", - "symfony/event-dispatcher": "5.4.* || 6.*", - "ramsey/uuid": "^4.2.3", - "moneyphp/money": "3.* || 4.*", - "symfony/uid": "6.*", - "ext-intl": "*" + "ramsey/uuid": "4.7.*", + "moneyphp/money": "4.3.*", + "symfony/http-client": "7.0.*", + "symfony/http-client-contracts": "3.4.*", + "symfony/http-foundation": "7.0.*", + "symfony/event-dispatcher": "7.0.*", + "symfony/uid": "7.0.*" }, "require-dev": { - "monolog/monolog": "2.1.*", - "symfony/console": "5.4.* || 6.*", - "symfony/dotenv": "5.4.* || 6.*", - "symfony/debug-bundle": "5.4.* || 6.*", - "phpstan/phpstan": "1.*", - "phpunit/phpunit": "9.5.*", - "symfony/stopwatch": "5.4.* || 6.*", + "monolog/monolog": "2.9.*", + "phpstan/phpstan": "1.10.*", + "phpunit/phpunit": "10.5.*", + "symfony/console": "7.0.*", + "symfony/dotenv": "7.0.*", + "symfony/debug-bundle": "7.0.*", + "symfony/stopwatch": "7.0.*", "roave/security-advisories": "dev-master" }, "autoload": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index a3e33d9d..eb16a1a7 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,25 +1,19 @@ - - - - ./src - - - - - - - - ./tests/Unit - - - ./tests/Integration - - + + + + + + + ./tests/Unit + + + ./tests/Integration + + + + + ./src + + diff --git a/src/Services/CRM/Contact/Service/Batch.php b/src/Services/CRM/Contact/Service/Batch.php index f2981421..0d929947 100644 --- a/src/Services/CRM/Contact/Service/Batch.php +++ b/src/Services/CRM/Contact/Service/Batch.php @@ -121,8 +121,8 @@ class Batch extends AbstractBatchService * WEB?: string, * IM?: string, * } $filter - * @param array $select = ['ID','HONORIFIC','NAME','SECOND_NAME','LAST_NAME','PHOTO','BIRTHDATE','TYPE_ID','SOURCE_ID','SOURCE_DESCRIPTION','POST','ADDRESS','ADDRESS_2','ADDRESS_CITY','ADDRESS_POSTAL_CODE','ADDRESS_REGION','ADDRESS_PROVINCE','ADDRESS_COUNTRY','ADDRESS_COUNTRY_CODE','ADDRESS_LOC_ADDR_ID','COMMENTS','OPENED','EXPORT','HAS_PHONE','HAS_EMAIL','HAS_IMOL','ASSIGNED_BY_ID','CREATED_BY_ID','MODIFY_BY_ID','DATE_CREATE','DATE_MODIFY','COMPANY_ID','COMPANY_IDS','LEAD_ID','ORIGINATOR_ID','ORIGIN_ID','ORIGIN_VERSION','FACE_ID','UTM_SOURCE','UTM_MEDIUM','UTM_CAMPAIGN','UTM_CONTENT','UTM_TERM','PHONE','EMAIL','WEB','IM'] - * @param int|null $limit + * @param array $select = ['ID','HONORIFIC','NAME','SECOND_NAME','LAST_NAME','PHOTO','BIRTHDATE','TYPE_ID','SOURCE_ID','SOURCE_DESCRIPTION','POST','ADDRESS','ADDRESS_2','ADDRESS_CITY','ADDRESS_POSTAL_CODE','ADDRESS_REGION','ADDRESS_PROVINCE','ADDRESS_COUNTRY','ADDRESS_COUNTRY_CODE','ADDRESS_LOC_ADDR_ID','COMMENTS','OPENED','EXPORT','HAS_PHONE','HAS_EMAIL','HAS_IMOL','ASSIGNED_BY_ID','CREATED_BY_ID','MODIFY_BY_ID','DATE_CREATE','DATE_MODIFY','COMPANY_ID','COMPANY_IDS','LEAD_ID','ORIGINATOR_ID','ORIGIN_ID','ORIGIN_VERSION','FACE_ID','UTM_SOURCE','UTM_MEDIUM','UTM_CAMPAIGN','UTM_CONTENT','UTM_TERM','PHONE','EMAIL','WEB','IM'] + * @param int|null $limit * * @return Generator * @throws BaseException @@ -220,9 +220,7 @@ public function add(array $contacts): Generator * 'params' => [] * ] * - * @param array $entityItems - * - * @param array $entityItems + * @param array $entityItems * @return Generator * @throws BaseException */ @@ -247,25 +245,4 @@ public function delete(array $contactId): Generator yield $key => new DeletedItemBatchResult($item); } } - - /** - * Update contact - * - * Update elements in array with structure - * element_id => [ // contact id - * 'fields' => [], // contact fields to update - * 'params' => [] - * ] - * - * @param array $entityItems - * - * @return \Generator - * @throws \Bitrix24\SDK\Core\Exceptions\BaseException - */ - public function update(array $entityItems): Generator - { - foreach ($this->batch->updateEntityItems('crm.contact.update', $entityItems) as $key => $item) { - yield $key => new UpdatedItemBatchResult($item); - } - } } \ No newline at end of file From 5d658288b721843795d1de3b7bd74099d4e475eb Mon Sep 17 00:00:00 2001 From: mesilov Date: Thu, 4 Jan 2024 17:44:25 +0600 Subject: [PATCH 84/94] add multifields Signed-off-by: mesilov --- CHANGELOG.md | 7 +- composer.json | 3 +- .../CRM/Common/Result/AbstractCrmItem.php | 43 +++++++- .../Result/SystemFields/Types/Email.php | 25 +++++ .../SystemFields/Types/EmailValueType.php | 13 +++ .../SystemFields/Types/InstantMessenger.php | 25 +++++ .../Types/InstantMessengerValueType.php | 22 ++++ .../Result/SystemFields/Types/Website.php | 25 +++++ .../SystemFields/Types/WebsiteValueType.php | 16 +++ .../CRM/Contact/Result/ContactItemResult.php | 9 +- .../CRM/Lead/Result/LeadItemResult.php | 12 ++- .../CRM/Contact/Service/ContactTest.php | 100 ++++++++++++++++++ 12 files changed, 286 insertions(+), 14 deletions(-) create mode 100644 src/Services/CRM/Common/Result/SystemFields/Types/Email.php create mode 100644 src/Services/CRM/Common/Result/SystemFields/Types/EmailValueType.php create mode 100644 src/Services/CRM/Common/Result/SystemFields/Types/InstantMessenger.php create mode 100644 src/Services/CRM/Common/Result/SystemFields/Types/InstantMessengerValueType.php create mode 100644 src/Services/CRM/Common/Result/SystemFields/Types/Website.php create mode 100644 src/Services/CRM/Common/Result/SystemFields/Types/WebsiteValueType.php diff --git a/CHANGELOG.md b/CHANGELOG.md index b945cb24..f4a5cdaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,13 @@ ### Added +* ❗️add php 8.3 support * add `Symfony\Component\Uid\Uuid` requirements * add contracts for bitrix24 applications based on bitrix24-php-sdk - `Bitrix24\SDK\Application\Contracts`, now added `Bitrix24Account` * add [service builder factory](https://github.com/mesilov/bitrix24-php-sdk/issues/328) * add method `Bitrix24\SDK\Core\Credentials\Scope::initFromString` * add method `Bitrix24\SDK\Application\ApplicationStatus::initFromString` -* ❗️add php 8.2 support * add system CRM multi-field type `Bitrix24\SDK\Services\CRM\Common\Result\SystemFields\Types\Phone` * add scope `user`,`user_basic`,`user_brief`,`user.userfield` and services [add scope user support](https://github.com/mesilov/bitrix24-php-sdk/issues/339) @@ -25,6 +25,11 @@ * add enum `DealStageSemanticId` * add Duplicate search support for `Bitrix24\SDK\Services\CRM\Duplicates\Service\Duplicate` * add `x-request-id` [header support](https://github.com/mesilov/bitrix24-php-sdk/issues/354) +* add CRM multifields support [header support](https://github.com/mesilov/bitrix24-php-sdk/issues/338) + * `Email` + * `Phone` + * `Website` + * `IM` ### Changed diff --git a/composer.json b/composer.json index 67e5a23e..c6cf5d7c 100644 --- a/composer.json +++ b/composer.json @@ -43,7 +43,8 @@ "symfony/dotenv": "7.0.*", "symfony/debug-bundle": "7.0.*", "symfony/stopwatch": "7.0.*", - "roave/security-advisories": "dev-master" + "roave/security-advisories": "dev-master", + "fakerphp/faker": "1.23.*" }, "autoload": { "psr-4": { diff --git a/src/Services/CRM/Common/Result/AbstractCrmItem.php b/src/Services/CRM/Common/Result/AbstractCrmItem.php index 307fc025..f7d9c321 100644 --- a/src/Services/CRM/Common/Result/AbstractCrmItem.php +++ b/src/Services/CRM/Common/Result/AbstractCrmItem.php @@ -5,8 +5,11 @@ namespace Bitrix24\SDK\Services\CRM\Common\Result; use Bitrix24\SDK\Core\Result\AbstractItem; +use Bitrix24\SDK\Services\CRM\Common\Result\SystemFields\Types\Email; +use Bitrix24\SDK\Services\CRM\Common\Result\SystemFields\Types\InstantMessenger; use Bitrix24\SDK\Services\CRM\Common\Result\SystemFields\Types\Phone; use Bitrix24\SDK\Services\CRM\Common\Result\SystemFields\Types\PhoneValueType; +use Bitrix24\SDK\Services\CRM\Common\Result\SystemFields\Types\Website; use Bitrix24\SDK\Services\CRM\Userfield\Exceptions\UserfieldNotFoundException; use DateTimeImmutable; use Money\Currency; @@ -14,7 +17,7 @@ class AbstractCrmItem extends AbstractItem { - private const CRM_USERFIELD_PREFIX = 'UF_CRM_'; + private const string CRM_USERFIELD_PREFIX = 'UF_CRM_'; /** * @var Currency @@ -32,7 +35,7 @@ public function __construct(array $data, Currency $currency = null) /** * @param int|string $offset * - * @return bool|\DateTimeImmutable|int|mixed|null + * @return bool|DateTimeImmutable|int|mixed|null */ public function __get($offset) @@ -132,8 +135,38 @@ public function __get($offset) } $items = []; - foreach ($this->data[$offset] as $phone) { - $items[] = new Phone($phone); + foreach ($this->data[$offset] as $item) { + $items[] = new Phone($item); + } + return $items; + case 'EMAIL': + if (!$this->isKeyExists($offset)) { + return []; + } + + $items = []; + foreach ($this->data[$offset] as $item) { + $items[] = new Email($item); + } + return $items; + case 'WEB': + if (!$this->isKeyExists($offset)) { + return []; + } + + $items = []; + foreach ($this->data[$offset] as $item) { + $items[] = new Website($item); + } + return $items; + case 'IM': + if (!$this->isKeyExists($offset)) { + return []; + } + + $items = []; + foreach ($this->data[$offset] as $item) { + $items[] = new InstantMessenger($item); } return $items; case 'currencyId': @@ -154,7 +187,7 @@ public function __get($offset) */ protected function getKeyWithUserfieldByFieldName(string $fieldName) { - if(!str_starts_with($fieldName, self::CRM_USERFIELD_PREFIX)) { + if (!str_starts_with($fieldName, self::CRM_USERFIELD_PREFIX)) { $fieldName = self::CRM_USERFIELD_PREFIX . $fieldName; } if (!$this->isKeyExists($fieldName)) { diff --git a/src/Services/CRM/Common/Result/SystemFields/Types/Email.php b/src/Services/CRM/Common/Result/SystemFields/Types/Email.php new file mode 100644 index 00000000..603cdae9 --- /dev/null +++ b/src/Services/CRM/Common/Result/SystemFields/Types/Email.php @@ -0,0 +1,25 @@ + $this->data[$offset], + 'ID' => (int)$this->data['ID'], + 'VALUE_TYPE' => EmailValueType::from($this->data['VALUE_TYPE']), + default => parent::__get($offset), + }; + } +} \ No newline at end of file diff --git a/src/Services/CRM/Common/Result/SystemFields/Types/EmailValueType.php b/src/Services/CRM/Common/Result/SystemFields/Types/EmailValueType.php new file mode 100644 index 00000000..5f3bc5b2 --- /dev/null +++ b/src/Services/CRM/Common/Result/SystemFields/Types/EmailValueType.php @@ -0,0 +1,13 @@ + $this->data[$offset], + 'ID' => (int)$this->data['ID'], + 'VALUE_TYPE' => EmailValueType::from($this->data['VALUE_TYPE']), + default => parent::__get($offset), + }; + } +} \ No newline at end of file diff --git a/src/Services/CRM/Common/Result/SystemFields/Types/InstantMessengerValueType.php b/src/Services/CRM/Common/Result/SystemFields/Types/InstantMessengerValueType.php new file mode 100644 index 00000000..87b5c223 --- /dev/null +++ b/src/Services/CRM/Common/Result/SystemFields/Types/InstantMessengerValueType.php @@ -0,0 +1,22 @@ + $this->data[$offset], + 'ID' => (int)$this->data['ID'], + 'VALUE_TYPE' => EmailValueType::from($this->data['VALUE_TYPE']), + default => parent::__get($offset), + }; + } +} \ No newline at end of file diff --git a/src/Services/CRM/Common/Result/SystemFields/Types/WebsiteValueType.php b/src/Services/CRM/Common/Result/SystemFields/Types/WebsiteValueType.php new file mode 100644 index 00000000..dd9f322d --- /dev/null +++ b/src/Services/CRM/Common/Result/SystemFields/Types/WebsiteValueType.php @@ -0,0 +1,16 @@ +assertEquals($totalBefore + $newContactsCount, $totalAfter); } + /** + * @return void + * @covers Contact::get + * @covers Contact::add + * @throws Core\Exceptions\TransportException + * @throws Core\Exceptions\BaseException + */ + public function testGetEmail(): void + { + $email = $this->faker->email(); + $this->assertEquals($email, + $this->contactService->get( + $this->contactService->add([ + 'NAME' => $this->faker->name(), + 'EMAIL' => [ + [ + 'VALUE' => $email, + 'VALUE_TYPE' => EmailValueType::work->name, + ] + ], + ])->getId())->contact()->EMAIL[0]->VALUE); + } + + /** + * @return void + * @covers Contact::get + * @covers Contact::add + * @throws Core\Exceptions\TransportException + * @throws Core\Exceptions\BaseException + */ + public function testGetPhone(): void + { + $phone = $this->faker->e164PhoneNumber(); + $this->assertEquals($phone, + $this->contactService->get( + $this->contactService->add([ + 'NAME' => $this->faker->name(), + 'PHONE' => [ + [ + 'VALUE' => $phone, + 'VALUE_TYPE' => PhoneValueType::work->name, + ] + ], + ])->getId())->contact()->PHONE[0]->VALUE); + } + + /** + * @return void + * @covers Contact::get + * @covers Contact::add + * @throws Core\Exceptions\TransportException + * @throws Core\Exceptions\BaseException + */ + public function testGetInstantMessenger(): void + { + $phone = $this->faker->e164PhoneNumber(); + $this->assertEquals($phone, + $this->contactService->get( + $this->contactService->add([ + 'NAME' => $this->faker->name(), + 'IM' => [ + [ + 'VALUE' => $phone, + 'VALUE_TYPE' => InstantMessengerValueType::telegram->name, + ] + ], + ])->getId())->contact()->IM[0]->VALUE); + } + + /** + * @return void + * @covers Contact::get + * @covers Contact::add + * @throws Core\Exceptions\TransportException + * @throws Core\Exceptions\BaseException + */ + public function testGetWebsite(): void + { + $url = $this->faker->url(); + $this->assertEquals($url, + $this->contactService->get( + $this->contactService->add([ + 'NAME' => $this->faker->name(), + 'WEB' => [ + [ + 'VALUE' => $url, + 'VALUE_TYPE' => WebsiteValueType::work, + ] + ], + ])->getId())->contact()->WEB[0]->VALUE); + } + public function setUp(): void { $this->contactService = Fabric::getServiceBuilder()->getCRMScope()->contact(); + $this->faker = Faker\Factory::create(); } } \ No newline at end of file From 644a0ad429ab59217140a25d61201c710edcd339 Mon Sep 17 00:00:00 2001 From: mesilov Date: Fri, 12 Jan 2024 23:47:35 +0600 Subject: [PATCH 85/94] change query string parameter name Signed-off-by: mesilov --- .../HttpClient/RequestId/DefaultRequestIdGenerator.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php b/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php index 1fde5608..9b49016d 100644 --- a/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php +++ b/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php @@ -8,9 +8,9 @@ class DefaultRequestIdGenerator implements RequestIdGeneratorInterface { - private const DEFAULT_REQUEST_ID_HEADER_FIELD_NAME = 'X-Request-ID'; - private const DEFAULT_QUERY_STRING_PARAMETER_NAME = 'request_id'; - private const KEY_NAME_VARIANTS = [ + private const string DEFAULT_REQUEST_ID_HEADER_FIELD_NAME = 'X-Request-ID'; + private const string DEFAULT_QUERY_STRING_PARAMETER_NAME = 'bx24_request_id'; + private const array KEY_NAME_VARIANTS = [ 'REQUEST_ID', 'HTTP_X_REQUEST_ID', 'UNIQUE_ID' From 3b1abfac4ff7790feda12f60b26b922373d6f263 Mon Sep 17 00:00:00 2001 From: mesilov Date: Mon, 5 Feb 2024 00:34:18 +0600 Subject: [PATCH 86/94] Implement catalog scope services New classes related to Catalog scope services have been created and integrated into the existing structure. This includes product-related result classes `ProductResult`, `ProductsResult`, `ProductItemResult` and service classes `Product` and `Batch`. Additionally, a `CatalogServiceBuilder` is added to construct the catalog service, and a `ProductType` enumeration has been defined to set product types. The commit also includes an update in the `CHANGELOG.md`. Signed-off-by: mesilov --- CHANGELOG.md | 1 + .../Catalog/CatalogServiceBuilder.php | 23 ++++ src/Services/Catalog/Common/ProductType.php | 14 +++ .../Common/Result/AbstractCatalogItem.php | 105 ++++++++++++++++++ .../Product/Result/ProductItemResult.php | 57 ++++++++++ .../Catalog/Product/Result/ProductResult.php | 15 +++ .../Catalog/Product/Result/ProductsResult.php | 25 +++++ .../Catalog/Product/Service/Batch.php | 22 ++++ .../Catalog/Product/Service/Product.php | 83 ++++++++++++++ src/Services/ServiceBuilder.php | 15 ++- 10 files changed, 355 insertions(+), 5 deletions(-) create mode 100644 src/Services/Catalog/CatalogServiceBuilder.php create mode 100644 src/Services/Catalog/Common/ProductType.php create mode 100644 src/Services/Catalog/Common/Result/AbstractCatalogItem.php create mode 100644 src/Services/Catalog/Product/Result/ProductItemResult.php create mode 100644 src/Services/Catalog/Product/Result/ProductResult.php create mode 100644 src/Services/Catalog/Product/Result/ProductsResult.php create mode 100644 src/Services/Catalog/Product/Service/Batch.php create mode 100644 src/Services/Catalog/Product/Service/Product.php diff --git a/CHANGELOG.md b/CHANGELOG.md index f4a5cdaf..edac8b24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ * `Phone` * `Website` * `IM` +* add [Catalog](https://github.com/mesilov/bitrix24-php-sdk/issues/364) scope services support ### Changed diff --git a/src/Services/Catalog/CatalogServiceBuilder.php b/src/Services/Catalog/CatalogServiceBuilder.php new file mode 100644 index 00000000..108db59d --- /dev/null +++ b/src/Services/Catalog/CatalogServiceBuilder.php @@ -0,0 +1,23 @@ +serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Catalog\Product\Service\Product( + new Catalog\Product\Service\Batch($this->batch, $this->log), + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } +} \ No newline at end of file diff --git a/src/Services/Catalog/Common/ProductType.php b/src/Services/Catalog/Common/ProductType.php new file mode 100644 index 00000000..0972c1a2 --- /dev/null +++ b/src/Services/Catalog/Common/ProductType.php @@ -0,0 +1,14 @@ +currency = $currency; + } + } + + /** + * @param int|string $offset + * + * @return bool|DateTimeImmutable|int|mixed|null + */ + + public function __get($offset) + { + switch ($offset) { + case 'active': + case 'available': + case 'bundle': + return $this->data[$offset] === 'Y'; + case 'barcodeMulti': + case 'canBuyZero': + if ($this->data[$offset] !== null) { + return $this->data[$offset] === 'Y'; + } + return null; + case 'code': + case 'detailText': + case 'detailTextType': + case 'name': + case 'previewText': + case 'previewTextType': + case 'xmlId': + return (string)$this->data[$offset]; + case 'createdBy': + case 'iblockId': + case 'iblockSectionId': + case 'id': + case 'modifiedBy': + case 'sort': + case 'height': + case 'length': + if ($this->data[$offset] !== '' && $this->data[$offset] !== null) { + return (int)$this->data[$offset]; + } + break; + case 'dateActiveFrom': + case 'dateActiveTo': + case 'dateCreate': + case 'timestampX': + if ($this->data[$offset] !== '') { + return DateTimeImmutable::createFromFormat(DATE_ATOM, $this->data[$offset]); + } + + return null; + case 'type': + return ProductType::from($this->data[$offset]); + } + + return $this->data[$offset] ?? null; + } + + /** + * get userfield by field name + * + * @param string $fieldName + * + * @return mixed|null + * @throws UserfieldNotFoundException + */ + protected function getKeyWithUserfieldByFieldName(string $fieldName) + { + if (!str_starts_with($fieldName, self::CRM_USERFIELD_PREFIX)) { + $fieldName = self::CRM_USERFIELD_PREFIX . $fieldName; + } + if (!$this->isKeyExists($fieldName)) { + throw new UserfieldNotFoundException(sprintf('crm userfield not found by field name %s', $fieldName)); + } + + return $this->$fieldName; + } +} \ No newline at end of file diff --git a/src/Services/Catalog/Product/Result/ProductItemResult.php b/src/Services/Catalog/Product/Result/ProductItemResult.php new file mode 100644 index 00000000..475c4bff --- /dev/null +++ b/src/Services/Catalog/Product/Result/ProductItemResult.php @@ -0,0 +1,57 @@ +getKeyWithUserfieldByFieldName($userfieldName); + } +} \ No newline at end of file diff --git a/src/Services/Catalog/Product/Result/ProductResult.php b/src/Services/Catalog/Product/Result/ProductResult.php new file mode 100644 index 00000000..3e5564fe --- /dev/null +++ b/src/Services/Catalog/Product/Result/ProductResult.php @@ -0,0 +1,15 @@ +getCoreResponse()->getResponseData()->getResult()['product']); + } +} \ No newline at end of file diff --git a/src/Services/Catalog/Product/Result/ProductsResult.php b/src/Services/Catalog/Product/Result/ProductsResult.php new file mode 100644 index 00000000..9f80be4e --- /dev/null +++ b/src/Services/Catalog/Product/Result/ProductsResult.php @@ -0,0 +1,25 @@ +getCoreResponse()->getResponseData()->getResult()['products'] as $product) { + $res[] = new ProductItemResult($product); + } + + return $res; + } +} \ No newline at end of file diff --git a/src/Services/Catalog/Product/Service/Batch.php b/src/Services/Catalog/Product/Service/Batch.php new file mode 100644 index 00000000..1bd42ef6 --- /dev/null +++ b/src/Services/Catalog/Product/Service/Batch.php @@ -0,0 +1,22 @@ +core->call('catalog.product.get', ['id' => $productId])); + } + + /** + * The method gets list of commercial catalog products by filter. + * + * @see https://training.bitrix24.com/rest_help/catalog/product/catalog_product_list.php + * @throws TransportException + * @throws BaseException + */ + public function list(array $select, array $filter, array $order, int $start): ProductsResult + { + return new ProductsResult($this->core->call('catalog.product.list', [ + 'select' => $select, + 'filter' => $filter, + 'order' => $order, + 'start' => $start + ])); + } + + /** + * The method returns commercial catalog product fields by filter. + * @see https://training.bitrix24.com/rest_help/catalog/product/catalog_product_getfieldsbyfilter.php + * + * @param int $iblockId + * @param ProductType $productType + * @param array|null $additionalFilter + * @return FieldsResult + * @throws BaseException + * @throws TransportException + */ + public function fieldsByFilter(int $iblockId, ProductType $productType, ?array $additionalFilter = null): FieldsResult + { + $filter = [ + 'iblockId' => $iblockId, + 'productType' => $productType->value + ]; + if ($additionalFilter !== null) { + $filter = array_merge($filter, $additionalFilter); + } + + return new FieldsResult($this->core->call('catalog.product.getFieldsByFilter', ['filter' => $filter])); + } +} \ No newline at end of file diff --git a/src/Services/ServiceBuilder.php b/src/Services/ServiceBuilder.php index e603ce1f..2f7b1ca3 100644 --- a/src/Services/ServiceBuilder.php +++ b/src/Services/ServiceBuilder.php @@ -4,6 +4,7 @@ namespace Bitrix24\SDK\Services; +use Bitrix24\SDK\Services\Catalog\CatalogServiceBuilder; use Bitrix24\SDK\Services\CRM\CRMServiceBuilder; use Bitrix24\SDK\Services\IM\IMServiceBuilder; use Bitrix24\SDK\Services\IMOpenLines\IMOpenLinesServiceBuilder; @@ -13,11 +14,6 @@ use Bitrix24\SDK\Services\UserConsent\UserConsentServiceBuilder; use Bitrix24\SDK\Services\Placement\PlacementServiceBuilder; -/** - * Class ServiceBuilder - * - * @package Bitrix24\SDK\Services - */ class ServiceBuilder extends AbstractServiceBuilder { /** @@ -104,6 +100,15 @@ public function getPlacementScope(): PlacementServiceBuilder return $this->serviceCache[__METHOD__]; } + public function getCatalogScope(): CatalogServiceBuilder + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new CatalogServiceBuilder($this->core, $this->batch, $this->bulkItemsReader, $this->log); + } + + return $this->serviceCache[__METHOD__]; + } + /** * @return TelephonyServiceBuilder */ From fe5a91ce4ed1b4ad417a41c07b283ed8584f7396 Mon Sep 17 00:00:00 2001 From: mesilov Date: Sat, 17 Feb 2024 18:54:43 +0600 Subject: [PATCH 87/94] Update GitHub workflows for multi-OS testing Updated workflow configurations to support both Ubuntu and Windows OS. The changes cover PHPUnit tests, PHPStan, Vendor integration, and Integration tests by adding matrix-operating-system variables. These modifications also include updates in the supported PHP versions, and disabling the "fail-fast" strategy to ensure tests across all OS complete before reporting. Signed-off-by: mesilov --- .github/workflows/integration.yml | 10 +++++----- .github/workflows/phpstan.yml | 4 +++- .github/workflows/phpunit.yml | 6 ++++-- .github/workflows/vendor-check.yml | 6 +++--- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index bb8210c7..2a6b4634 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -9,19 +9,19 @@ on: env: COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --prefer-dist" BITRIX24_PHP_SDK_PLAYGROUND_WEBHOOK: ${{ secrets.BITRIX24_PHP_SDK_PLAYGROUND_WEBHOOK }} - TEST2_ENV: 12345 jobs: tests: name: "Integration tests" - - runs-on: ubuntu-latest - + runs-on: ${{ matrix.operating-system }} strategy: + fail-fast: false matrix: php-version: - - "8.1" + - "8.2" + - "8.3" dependencies: [ highest ] + operating-system: [ ubuntu-latest, windows-latest ] steps: diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 70847366..64ea73b4 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -7,14 +7,16 @@ name: PHPStan checks jobs: static-analysis: name: "PHPStan" - runs-on: "ubuntu-latest" + runs-on: ${{ matrix.operating-system }} strategy: fail-fast: false matrix: php-version: + - "8.2" - "8.3" dependencies: [ highest ] + operating-system: [ ubuntu-latest, windows-latest ] steps: - name: "Checkout" diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 14797cdc..00dc1d47 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -1,8 +1,8 @@ name: "PHPUnit tests" on: - - push - - pull_request + push: + pull_request: env: COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --prefer-dist" @@ -14,8 +14,10 @@ jobs: runs-on: ${{ matrix.operating-system }} strategy: + fail-fast: false matrix: php-version: + - "8.2" - "8.3" dependencies: [ highest ] operating-system: [ ubuntu-latest, windows-latest ] diff --git a/.github/workflows/vendor-check.yml b/.github/workflows/vendor-check.yml index beaa80f5..74662014 100644 --- a/.github/workflows/vendor-check.yml +++ b/.github/workflows/vendor-check.yml @@ -8,20 +8,20 @@ on: env: COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --prefer-dist" BITRIX24_PHP_SDK_PLAYGROUND_WEBHOOK: ${{ secrets.BITRIX24_PHP_SDK_PLAYGROUND_WEBHOOK }} - TEST2_ENV: 12345 jobs: tests: name: "Vendor integration tests" - runs-on: ubuntu-latest + runs-on: ${{ matrix.operating-system }} strategy: matrix: php-version: - - "8.1" - "8.2" + - "8.3" dependencies: [ highest ] + operating-system: [ ubuntu-latest, windows-latest ] steps: From 9c977bceb4497693d46ab24e2eac98a19de72af5 Mon Sep 17 00:00:00 2001 From: mesilov Date: Sat, 17 Feb 2024 19:14:11 +0600 Subject: [PATCH 88/94] Update PHP requirements and refactor README This commit updates the PHP version requirement in composer.json to allow either 8.2.* or 8.3.*, reflecting its compatibility with both. Moreover, the README file has been significantly refactored, removing unnecessary content, correcting a typo, and reorganizing the architecture section. Signed-off-by: mesilov --- CHANGELOG.md | 2 +- README.md | 248 ++++---------------------------------------------- composer.json | 2 +- 3 files changed, 20 insertions(+), 232 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edac8b24..e31e28c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Added -* ❗️add php 8.3 support +* ❗️add php 8.3 support, drop 8.1 and 8.0 support * add `Symfony\Component\Uid\Uuid` requirements * add contracts for bitrix24 applications based on bitrix24-php-sdk - `Bitrix24\SDK\Application\Contracts`, now added `Bitrix24Account` diff --git a/README.md b/README.md index 97104ce4..27a56e2c 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,6 @@ A powerful PHP library for the Bitrix24 REST API Integration tests run in GitHub actions with real Bitrix24 portal - -### BITRIX24-PHP-SDK Documentation - -- [Russian](/docs/RU/documentation.md) -- [English](/docs/EN/documentation.md) - ### BITRIX24-PHP-SDK ✨FEATURES✨ Support both auth modes: @@ -65,7 +59,7 @@ Performance improvements 🚀 - Performance first: - minimal impact on client code - ability to work with large amounts of data with constant memory consumption - - efficient operation of the API using butch requests + - efficient operation of the API using batch requests - Modern technology stack - based on [Symfony HttpClient](https://symfony.com/doc/current/http_client.html) - actual PHP versions language features @@ -77,48 +71,12 @@ Performance improvements 🚀 Help bitrix24-php-sdk by [boosty.to/bitrix24-php-sdk](https://boosty.to/bitrix24-php-sdk) its development! -### Architecture - -### Abstraction layers - -``` -- http protocol -- json data -- symfony http client -- \Bitrix24\SDK\Core\ApiClient - work with b24 rest-api endpoints - input: arrays \ strings - output: Symfony\Contracts\HttpClient\ResponseInterface, operate with strings - process: network operations -- \Bitrix24\SDK\Services\Main - work with b24 rest-api entities - input: arrays \ strings (?) or queries? - output: b24 response dto - process: b24 entities, operate with -``` - -### File Structure - -``` - /Core - ApiClient.php - default api-client, work on http abstraction layer, return - Symfony\Contracts\HttpClient\ResponseInterface - /Services - /CRM - /Deals - /Client - Deals.php - /Exceptions - /Tasks - … - Main.php - default bitrix24 rest api service provide basic funcions, work with data transfer objects -``` - ### Requirements -- php: >=7.4 +- php: >=8.2 - ext-json: * - ext-curl: * -### Example - ### Installation Add `"mesilov/bitrix24-php-sdk": "2.x"` to `composer.json` of your application. Or clone repo to your project. @@ -198,194 +156,24 @@ email: [Register new Bitrix24 account](https://www.bitrix24.ru/create.php?p=255670) -## Русский - -### Принципы по которым ведётся разработка - -- хороший DX (Developer Experience) - - автодополнение методов на уровне IDE - - типизированные сигнатуры вызова методов - - типизированные результаты вызова методов – используются нативные типы: int, array, bool, string - - хелперы для типовых операций -- хорошая документация - - документация по работе конкретного метода содержащая ссылку на офф документацию - - документация по работе с SDK -- производительность: - - минимальное влияние на клиентский код - - возможность работать с большими объёмами данных с константным потреблением памяти - - эффективная работа c API с использованием батч-запросов -- современный стек технологий: - - библиотеки для работы с сетью и возможностью асинхронной работы - - фичи новых версий PHP -- надёжной: - - покрытие тестами: unit, интеграционные, контрактные - - есть типовые примеры характерные для разных режимов работы и они оптимизированы по памяти \ быстродействию - -### Спонсоры - -Помогите развитию bitrix24-php-sdk подписавшись на [boosty.to/bitrix24-php-sdk](https://boosty.to/bitrix24-php-sdk)! - -### Ключевые особенности - -### Слои SDK - -### Service – API-интерфейс для работы с конкретной сущностью - -Зона ответственности: - -- контракт на API-методы сущности - -Входящие данные: - -- сигнатура вызова конкретного API-метода - -Возвращаемый результат: - -- `Core\Response` (????) **к обсуждению** - -В зависимости от метода может быть разный возвращаемый результат: - -- результат выполнения операции типа bool -- идентификатор созданной сущности типа int -- сущность + пользовательские поля с префиксами UF_ типа array -- массив сущностей типа array -- пустой массив как результат пустого фильтра. - -Если возвращать `Core\Response`, то в клиентском коде будут проблемы: - -- длинные цепочки в клиентском коде для получения возвращаемого результата - -```php -// добавили сделку в Б24 -$dealId = $dealsService->add($newDeal)->getResponseData()->getResult()->getResultData()[0]; -// получили массив сделок -$deals = $dealsService->list([], [], [], 0)->getResponseData()->getResult()->getResultData(); -``` - -- отсутствие релевантной вызываемому методу типизации возвращаемого результата. - -Ожидание: - -```php - add(array $newDeal):int // идентификатор новой сделки - list(array $order, array $filter, array $select, int $start):array //массив сделок + постраничка - get(int $dealId):array // конкретная сделка -``` - -Текущая реализация — возвращается унифицированный результат: - -```php -add(array $newDeal):Core\Response -list(array $order, array $filter, array $select, int $start):Core\Response -``` - -#### Core – вызов произвольных API-методов - -Зона ответственности: - -- вызов **произвольных** API-методов -- обработка ошибок уровня API -- запрос нового токена и повторение запроса, если получили ошибку `expired_token` - -Входящие данные: - -- `string $apiMethod` – название api-метода -- `array $parameters = []` – аргументы метода -Возвращаемый результат: `Core\Response` – **унифицированный** объект-обёртка, содержит: - -- `Symfony\Contracts\HttpClient\ResponseInterface` — объект ответа от сервера, может быть асинхронным -- `Core\Commands\Command` — информация о команде\аргументах которая была исполнена, используется при разборе пакетных запросов. - -Для получения результата запроса к API используется метод `Response::getResponseData`, который декодирует тело ответа вызвав -метод `Symfony\Contracts\HttpClient::toArray` -Возвращается стандартизированный DTO `ResponseData` от API-сервера с полями: - -- `Result` - DTO c результатом исполнения запроса; -- `Time` — DTO c таймингом прохождения запроса через сервера Битрикс24; -- `Pagination` — DTO постраничной навигации с полями `next` и `total`; - -В случае обнаружения ошибок уровня домена будет выброшено соответствующее типизированное исключение. - -Объект `Result` содержит метод `getResultData`, который возвращает массив с результатом исполнения API-запроса. В зависимости от вызванного -метода там может быть: - -- результат выполнения операции типа bool -- идентификатор созданной сущности типа int -- сущность + пользовательские поля с префиксами UF_ типа array -- массив сущностей типа array -- пустой массив как результат пустого фильтра. - -#### ApiClient — работа с сетью и эндпоинтами API-серверов - -Зона ответственности: - -- передача данных по сети -- соблюдение контракта на эндпоинты с которыми идёт работы -- «подпись» запросов токенами \ передача их в нужные входящие адреса если используется авторизация по вебхукам - -Используется: -Symfony HttpClient - -Входящие данные: - -- тип http-запроса -- массив с параметрами - -Возвращаемые результаты: -— `Symfony\Contracts\HttpClient\ResponseInterface` - -#### Формат передачи данных по сети - -JSON по HTTP/2 или HTTP/1.1 - -## Спонсоры - -### Тесты - -Тесты расположены в папке `tests` и бывают двух типов: юнит и интеграционные. -В папке `tests` создайте файл `.env.local` и заполните переменные из файла `.env`. - -#### Юнит тесты - -**Быстрые**, выполняются без сетевого взаимодействия с Битрикс 24. - -```shell -composer phpunit-run-unit-test -``` - -#### Интеграционные тесты - -**Медленные** тесты покрывают полный жизненный цикл CRUD операций подключение к Битрикс 24 происходи с помощью веб-хука. - -❗ Не запускайте интеграционные тесты на ваших production порталах они удалят все ваши данные ❗️ - -Для запуска интеграционных тестов вам нужно: - -1. Создать [Новый портал Битрикс 24](https://www.bitrix24.ru/create.php?p=255670) для запуска тестов. -2. Перейти в левое меню и нажать "Карта сайта". -3. Найти меню для "Разработчиков" -4. Кликнуть в меню «Другое» -5. Кликнуть в меню «Входящий веб-хук» -6. Выбрать все нужные расширения и нажать кнопку "сохранить". -7. Создать файл `/tests/.env.local` с переменными окружения которые скопировать из файла `/tests/.env` . - -```yaml -APP_ENV=dev -BITRIX24_WEBHOOK=https:// your portal webhook url -INTEGRATION_TEST_LOG_LEVEL=500 -``` +### Architecture -8. Запуск из командной строки. +#### Abstraction layers -```shell -composer composer phpunit-run-integration-tests ``` - -#### Статический анализ кодовой базы – phpstan - -Запуск из командной строки. - -```shell - composer phpstan-analyse +- http protocol +- json data +- symfony http client +- \Bitrix24\SDK\Core\ApiClient - work with b24 rest-api endpoints + input: arrays \ strings + output: Symfony\Contracts\HttpClient\ResponseInterface, operate with strings + process: network operations +- \Bitrix24\SDK\Services\* - work with b24 rest-api entities + input: arrays \ strings + output: b24 response dto + process: b24 entities, operate with immutable objects ``` +#### Symfony HttpClient +#### Core +#### ApiClient diff --git a/composer.json b/composer.json index c6cf5d7c..0aba0659 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "sort-packages": true }, "require": { - "php": "8.3.*", + "php": "8.2.* || 8.3.*", "ext-json": "*", "ext-bcmath": "*", "ext-curl": "*", From e649a3ca35f1445819e4679724d477b8738d43d5 Mon Sep 17 00:00:00 2001 From: mesilov Date: Sun, 18 Feb 2024 22:48:28 +0600 Subject: [PATCH 89/94] Add catalog service functions and product service methods to SDK This update adds new catalog service-related functions in the CatalogServiceBuilder file and introduces several methods for product service. These methods include functions to add, delete, and retrieve products. Additionally, several integration tests have been introduced to ensure the correct functioning of these new methods. Also, an example phpstan static analyzer command's been added to Makefile, and PHP requirement has been adjusted to support PHP 8.2. Plus, the README file has been refactored for clarity and better organization. Signed-off-by: mesilov --- Makefile | 6 + README.md | 93 +++++----- .../Catalog/Result/CatalogItemResult.php | 23 +++ .../Catalog/Catalog/Result/CatalogResult.php | 15 ++ .../Catalog/Catalog/Result/CatalogsResult.php | 26 +++ .../Catalog/Catalog/Service/Catalog.php | 64 +++++++ .../Catalog/CatalogServiceBuilder.php | 13 ++ .../Product/Result/ProductItemResult.php | 11 -- .../Catalog/Product/Result/ProductResult.php | 4 + .../Catalog/Product/Service/Product.php | 39 ++++- .../Catalog/Catalog/Service/CatalogTest.php | 62 +++++++ .../Catalog/Product/Service/ProductTest.php | 159 ++++++++++++++++++ 12 files changed, 452 insertions(+), 63 deletions(-) create mode 100644 Makefile create mode 100644 src/Services/Catalog/Catalog/Result/CatalogItemResult.php create mode 100644 src/Services/Catalog/Catalog/Result/CatalogResult.php create mode 100644 src/Services/Catalog/Catalog/Result/CatalogsResult.php create mode 100644 src/Services/Catalog/Catalog/Service/Catalog.php create mode 100644 tests/Integration/Services/Catalog/Catalog/Service/CatalogTest.php create mode 100644 tests/Integration/Services/Catalog/Product/Service/ProductTest.php diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..ee1097e7 --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +default: + @echo "make needs target:" + @egrep -e '^\S+' ./Makefile | grep -v default | sed -r 's/://' | sed -r 's/^/ - /' + +phpstan: + vendor/bin/phpstan analyse \ No newline at end of file diff --git a/README.md b/README.md index 27a56e2c..162cf165 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Bitrix24 REST API PHP SDK A powerful PHP library for the Bitrix24 REST API -### Build status +## Build status | CI\CD [status](https://github.com/mesilov/bitrix24-php-sdk/actions) on `master` | |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| @@ -15,20 +15,16 @@ A powerful PHP library for the Bitrix24 REST API Integration tests run in GitHub actions with real Bitrix24 portal -### BITRIX24-PHP-SDK ✨FEATURES✨ +## BITRIX24-PHP-SDK ✨FEATURES✨ Support both auth modes: - [x] work with auth tokens for Bitrix24 applications in marketplace - [x] work with incoming webhooks for simple integration projects for current portal -Low-level tools to devs: - -- Domain core events: - - [x] Access Token expired - - [ ] Bitrix24 portal domain url changed -- [ ] Rate-limit strategy -- [ ] Retry strategy for safe methods +Domain core events: + - [x] Access Token expired + - [x] Bitrix24 portal domain url changed API - level features @@ -46,7 +42,12 @@ Performance improvements 🚀 - [ ] composite batch queries to many entities (work in progress) - [ ] read without count flag -### Development principles +Low-level tools to devs: +- [ ] Rate-limit strategy +- [ ] Retry strategy for safe methods + + +## Development principles - Good developer experience - auto-completion of methods at the IDE @@ -67,26 +68,42 @@ Performance improvements 🚀 - test coverage: unit, integration, contract - typical examples typical for different modes of operation and they are optimized for memory \ performance -### Sponsors +## Architecture -Help bitrix24-php-sdk by [boosty.to/bitrix24-php-sdk](https://boosty.to/bitrix24-php-sdk) its development! +### Abstraction layers -### Requirements +``` +- http2 protocol via json data structures +- symfony http client +- \Bitrix24\SDK\Core\ApiClient - work with b24 rest-api endpoints + input: arrays \ strings + output: Symfony\Contracts\HttpClient\ResponseInterface, operate with strings + process: network operations +- \Bitrix24\SDK\Services\* - work with b24 rest-api entities + input: arrays \ strings + output: b24 response dto + process: b24 entities, operate with immutable objects +``` +## Sponsors + +Help bitrix24-php-sdk by [boosty.to/bitrix24-php-sdk](https://boosty.to/bitrix24-php-sdk) + +## Requirements - php: >=8.2 - ext-json: * - ext-curl: * -### Installation +## Installation Add `"mesilov/bitrix24-php-sdk": "2.x"` to `composer.json` of your application. Or clone repo to your project. -### Tests +## Tests Tests locate in folder `tests` and we have two test types. In folder tests create file `.env.local` and fill environment variables from `.env`. -#### Unit tests +### Unit tests **Fast**, in-memory tests without a network I\O For run unit tests you must call in command line @@ -94,11 +111,11 @@ In folder tests create file `.env.local` and fill environment variables from `.e composer phpunit-run-unit-test ``` -#### Integration tests +### Integration tests **Slow** tests with full lifecycle with your **test** Bitrix24 portal via webhook. -❗️Do not run integration tests with production portals ❗️ +❗️Do not run integration tests with production portals For run integration test you must: @@ -130,50 +147,28 @@ Call in command line composer phpstan-analyse ``` -### Submitting bugs and feature requests +## Submitting bugs and feature requests Bugs and feature request are tracked on [GitHub](https://github.com/mesilov/bitrix24-php-sdk/issues) -### License +## License bitrix24-php-sdk is licensed under the MIT License - see the `MIT-LICENSE.txt` file for details -### Author +## Authors -Maxim Mesilov - -
-See also the list of [contributors](https://github.com/mesilov/bitrix24-php-sdk/graphs/contributors) which participated in this project. +Maksim Mesilov - mesilov.maxim@gmail.com -### Need custom Bitrix24 application? ## +See also the list of [contributors](https://github.com/mesilov/bitrix24-php-sdk/graphs/contributors) which participated in this project. -email: +## Need custom Bitrix24 application? +mesilov.maxim@gmail.com for private consultations or dedicated support -### Documentation +## Documentation [Bitrix24 API documentation - Russian](http://dev.1c-bitrix.ru/rest_help/) [Bitrix24 API documentation - English](https://training.bitrix24.com/rest_help/) -[Register new Bitrix24 account](https://www.bitrix24.ru/create.php?p=255670) - - -### Architecture - -#### Abstraction layers - -``` -- http protocol -- json data -- symfony http client -- \Bitrix24\SDK\Core\ApiClient - work with b24 rest-api endpoints - input: arrays \ strings - output: Symfony\Contracts\HttpClient\ResponseInterface, operate with strings - process: network operations -- \Bitrix24\SDK\Services\* - work with b24 rest-api entities - input: arrays \ strings - output: b24 response dto - process: b24 entities, operate with immutable objects -``` -#### Symfony HttpClient -#### Core -#### ApiClient +[Register new Bitrix24 account](https://www.bitrix24.ru/create.php?p=255670) \ No newline at end of file diff --git a/src/Services/Catalog/Catalog/Result/CatalogItemResult.php b/src/Services/Catalog/Catalog/Result/CatalogItemResult.php new file mode 100644 index 00000000..7a406f88 --- /dev/null +++ b/src/Services/Catalog/Catalog/Result/CatalogItemResult.php @@ -0,0 +1,23 @@ +getCoreResponse()->getResponseData()->getResult()['catalog']); + } +} \ No newline at end of file diff --git a/src/Services/Catalog/Catalog/Result/CatalogsResult.php b/src/Services/Catalog/Catalog/Result/CatalogsResult.php new file mode 100644 index 00000000..44c9799d --- /dev/null +++ b/src/Services/Catalog/Catalog/Result/CatalogsResult.php @@ -0,0 +1,26 @@ +getCoreResponse()->getResponseData()->getResult()['catalogs'] as $product) { + $res[] = new ProductItemResult($product); + } + + return $res; + } +} \ No newline at end of file diff --git a/src/Services/Catalog/Catalog/Service/Catalog.php b/src/Services/Catalog/Catalog/Service/Catalog.php new file mode 100644 index 00000000..b09c7e61 --- /dev/null +++ b/src/Services/Catalog/Catalog/Service/Catalog.php @@ -0,0 +1,64 @@ +core->call('catalog.catalog.get', ['id' => $catalogId])); + } + + /** + * The method gets field value of commercial catalog product by ID. + * + * @see https://training.bitrix24.com/rest_help/catalog/catalog/catalog_catalog_list.php + * @param array $select + * @param array $filter + * @param array $order + * @param int $start + * @return CatalogsResult + * @throws BaseException + * @throws TransportException + */ + public function list(array $select, array $filter, array $order, int $start): CatalogsResult + { + return new CatalogsResult($this->core->call('catalog.catalog.list', [ + 'select' => $select, + 'filter' => $filter, + 'order' => $order, + 'start' => $start + ])); + } + + /** + * Retrieves the fields for the catalog. + * + * @return FieldsResult Returns an instance of FieldsResult. + * @throws BaseException Throws a BaseException if there is an error in the core call. + * @throws TransportException Throws a TransportException if there is an error in the transport process. + * @see https://training.bitrix24.com/rest_help/catalog/catalog/catalog_catalog_getfields.php + */ + public function fields(): FieldsResult + { + return new FieldsResult($this->core->call('catalog.catalog.getFields')); + } +} \ No newline at end of file diff --git a/src/Services/Catalog/CatalogServiceBuilder.php b/src/Services/Catalog/CatalogServiceBuilder.php index 108db59d..32347b8c 100644 --- a/src/Services/Catalog/CatalogServiceBuilder.php +++ b/src/Services/Catalog/CatalogServiceBuilder.php @@ -6,6 +6,7 @@ use Bitrix24\SDK\Services\AbstractServiceBuilder; use Bitrix24\SDK\Services\Catalog; + class CatalogServiceBuilder extends AbstractServiceBuilder { public function product(): Catalog\Product\Service\Product @@ -20,4 +21,16 @@ public function product(): Catalog\Product\Service\Product return $this->serviceCache[__METHOD__]; } + + public function catalog(): Catalog\Catalog\Service\Catalog + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Catalog\Catalog\Service\Catalog( + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } } \ No newline at end of file diff --git a/src/Services/Catalog/Product/Result/ProductItemResult.php b/src/Services/Catalog/Product/Result/ProductItemResult.php index 475c4bff..07349bb3 100644 --- a/src/Services/Catalog/Product/Result/ProductItemResult.php +++ b/src/Services/Catalog/Product/Result/ProductItemResult.php @@ -6,7 +6,6 @@ use Bitrix24\SDK\Services\Catalog\Common\ProductType; use Bitrix24\SDK\Services\Catalog\Common\Result\AbstractCatalogItem; -use Bitrix24\SDK\Services\CRM\Userfield\Exceptions\UserfieldNotFoundException; use DateTimeInterface; use Money\Currency; use Money\Money; @@ -44,14 +43,4 @@ */ class ProductItemResult extends AbstractCatalogItem { - /** - * @param string $userfieldName - * - * @return mixed|null - * @throws UserfieldNotFoundException - */ - public function getUserfieldByFieldName(string $userfieldName) - { - return $this->getKeyWithUserfieldByFieldName($userfieldName); - } } \ No newline at end of file diff --git a/src/Services/Catalog/Product/Result/ProductResult.php b/src/Services/Catalog/Product/Result/ProductResult.php index 3e5564fe..e9aef70e 100644 --- a/src/Services/Catalog/Product/Result/ProductResult.php +++ b/src/Services/Catalog/Product/Result/ProductResult.php @@ -10,6 +10,10 @@ class ProductResult extends AbstractResult { public function product(): ProductItemResult { + if (array_key_exists('element', $this->getCoreResponse()->getResponseData()->getResult())) { + // fix for catalog.product.add + return new ProductItemResult($this->getCoreResponse()->getResponseData()->getResult()['element']); + } return new ProductItemResult($this->getCoreResponse()->getResponseData()->getResult()['product']); } } \ No newline at end of file diff --git a/src/Services/Catalog/Product/Service/Product.php b/src/Services/Catalog/Product/Service/Product.php index c7c6e928..afb59635 100644 --- a/src/Services/Catalog/Product/Service/Product.php +++ b/src/Services/Catalog/Product/Service/Product.php @@ -4,21 +4,22 @@ namespace Bitrix24\SDK\Services\Catalog\Product\Service; - use Bitrix24\SDK\Core\Contracts\CoreInterface; use Bitrix24\SDK\Core\Exceptions\BaseException; use Bitrix24\SDK\Core\Exceptions\TransportException; -use Bitrix24\SDK\Core\Response\Response; +use Bitrix24\SDK\Core\Result\DeletedItemResult; use Bitrix24\SDK\Core\Result\FieldsResult; use Bitrix24\SDK\Services\AbstractService; use Bitrix24\SDK\Services\Catalog\Common\ProductType; -use Bitrix24\SDK\Services\Catalog\Product\Result\ProductItemResult; use Bitrix24\SDK\Services\Catalog\Product\Result\ProductResult; use Bitrix24\SDK\Services\Catalog\Product\Result\ProductsResult; + use Psr\Log\LoggerInterface; class Product extends AbstractService { + public Batch $batch; + public function __construct( Batch $batch, CoreInterface $core, @@ -26,6 +27,7 @@ public function __construct( ) { parent::__construct($core, $log); + $this->batch = $batch; } /** @@ -40,6 +42,37 @@ public function get(int $productId): ProductResult return new ProductResult($this->core->call('catalog.product.get', ['id' => $productId])); } + /** + * The method adds a commercial catalog product. + * + * @see https://training.bitrix24.com/rest_help/catalog/product/catalog_product_add.php + * @param array $productFields + * @return ProductResult + * @throws BaseException + * @throws TransportException + */ + public function add(array $productFields): ProductResult + { + return new ProductResult($this->core->call('catalog.product.add', [ + 'fields' => $productFields + ] + )); + } + + /** + * The method deletes commercial catalog product. + * + * @see https://training.bitrix24.com/rest_help/catalog/product/catalog_product_delete.php + * @param int $productId + * @return DeletedItemResult + * @throws BaseException + * @throws TransportException + */ + public function delete(int $productId): DeletedItemResult + { + return new DeletedItemResult($this->core->call('catalog.product.delete', ['id' => $productId])); + } + /** * The method gets list of commercial catalog products by filter. * diff --git a/tests/Integration/Services/Catalog/Catalog/Service/CatalogTest.php b/tests/Integration/Services/Catalog/Catalog/Service/CatalogTest.php new file mode 100644 index 00000000..dc106b5b --- /dev/null +++ b/tests/Integration/Services/Catalog/Catalog/Service/CatalogTest.php @@ -0,0 +1,62 @@ +assertIsArray($this->service->fields()->getFieldsDescription()); + } + + /** + * Test the List method. + * + * @return void + * @throws BaseException if there is a base exception occurred + * @throws TransportException if there is a transport exception occurred + * @covers \Bitrix24\SDK\Services\Catalog\Catalog\Service\Catalog::list + */ + public function testList(): void + { + $this->assertGreaterThan(1, $this->service->list([], [], [], 1)->getCatalogs()[0]->id); + } + + /** + * Retrieves a catalog using the `get` method and asserts that the retrieved catalog's ID matches the original catalog's ID. + * + * @return void + * @throws BaseException if there is a general exception. + * @throws TransportException if there is an exception during transport. + * @covers \Bitrix24\SDK\Services\Catalog\Catalog\Service\Catalog::get + */ + public function testGet(): void + { + $catalog = $this->service->list([], [], [], 1)->getCatalogs()[0]; + $this->assertEquals($catalog->id, $this->service->get($catalog->id)->catalog()->id); + } + + public function setUp(): void + { + $this->service = Fabric::getServiceBuilder()->getCatalogScope()->catalog(); + } +} \ No newline at end of file diff --git a/tests/Integration/Services/Catalog/Product/Service/ProductTest.php b/tests/Integration/Services/Catalog/Product/Service/ProductTest.php new file mode 100644 index 00000000..5628390e --- /dev/null +++ b/tests/Integration/Services/Catalog/Product/Service/ProductTest.php @@ -0,0 +1,159 @@ +catalogService->list([], [], [], 1)->getCatalogs()[0]->iblockId; + $this->assertIsArray($this->productService->fieldsByFilter( + $iblockId, + ProductType::simple + )->getFieldsDescription()); + } + + /** + * Adds a new product to the system and asserts that the product was added successfully. + * + * @return void + * @throws BaseException If there is a base exception thrown during the product addition process. + * @throws TransportException If there is a transport exception thrown during the product addition process. + * @covers Product::add() + */ + public function testAdd(): void + { + $iblockId = $this->catalogService->list([], [], [], 1)->getCatalogs()[0]->iblockId; + $fields = [ + 'iblockId' => $iblockId, + 'name' => sprintf('test product name %s', time()), + '' + ]; + $result = $this->productService->add($fields); + $this->assertEquals($fields['name'], $result->product()->name); + $this->productService->delete($result->product()->id); + } + + /** + * Retrieves a product from the system and asserts that the correct product was retrieved. + * + * @return void + * @throws BaseException If there is a base exception thrown during the product retrieval process. + * @throws TransportException If there is a transport exception thrown during the product retrieval process. + * @covers Product::get() + */ + public function testGet(): void + { + $iblockId = $this->catalogService->list([], [], [], 1)->getCatalogs()[0]->iblockId; + $fields = [ + 'iblockId' => $iblockId, + 'name' => sprintf('test product name %s', time()), + ]; + $result = $this->productService->add($fields); + $productGet = $this->productService->get($result->product()->id); + $this->assertEquals($result->product()->id, $productGet->product()->id); + $this->productService->delete($productGet->product()->id); + } + + /** + * Deletes a product from the system and asserts that the product was deleted successfully. + * + * @return void + * @throws BaseException If there is a base exception thrown during the product deletion process. + * @throws TransportException If there is a transport exception thrown during the product deletion process. + * @covers Product::delete() + * @testdox test Product::delete + */ + public function testDelete(): void + { + $iblockId = $this->catalogService->list([], [], [], 1)->getCatalogs()[0]->iblockId; + $fields = [ + 'iblockId' => $iblockId, + 'name' => sprintf('test product name %s', time()), + ]; + $result = $this->productService->add($fields); + $productGet = $this->productService->get($result->product()->id); + $this->assertEquals($result->product()->id, $productGet->product()->id); + $this->productService->delete($productGet->product()->id); + + $filteredProducts = $this->productService->list( + [ + 'id', + 'iblockId' + ], + [ + 'id' => $productGet->product()->id, + 'iblockId' => $iblockId + ], + [ + 'id' => 'asc' + ], + 1 + ); + $this->assertCount(0, $filteredProducts->getProducts()); + } + + /** + * Retrieves a list of products that match the specified filter criteria and asserts that the expected number of products is returned. + * + * @return void + * @throws BaseException If there is a base exception thrown during the process of listing products. + * @throws TransportException If there is a transport exception thrown during the process of listing products. + * @covers Product::list() + */ + public function testList():void + { + $iblockId = $this->catalogService->list([], [], [], 1)->getCatalogs()[0]->iblockId; + $fields = [ + 'iblockId' => $iblockId, + 'name' => sprintf('test product name %s', time()), + ]; + $result = $this->productService->add($fields); + $productGet = $this->productService->get($result->product()->id); + $this->assertEquals($result->product()->id, $productGet->product()->id); + $filteredProducts = $this->productService->list( + [ + 'id', + 'iblockId' + ], + [ + 'id' => $productGet->product()->id, + 'iblockId' => $iblockId + ], + [ + 'id' => 'asc' + ], + 1 + ); + $this->assertCount(1, $filteredProducts->getProducts()); + } + + public function setUp(): void + { + $this->productService = Fabric::getServiceBuilder()->getCatalogScope()->product(); + $this->catalogService = Fabric::getServiceBuilder()->getCatalogScope()->catalog(); + } +} \ No newline at end of file From ad94742e9ccfc3f7ce64f946af99fbc30bb447b0 Mon Sep 17 00:00:00 2001 From: mesilov Date: Sun, 18 Feb 2024 23:04:24 +0600 Subject: [PATCH 90/94] Change data provider methods to static in tests The data provider methods used in various test cases have been changed to static. This change was applied to "CredentialsTest", "ApplicationStatusTest", "ApplicationProfileTest", "DefaultRequestIdGeneratorTest", and "TimeTest". An additional command has also been added to the Makefile for running unit tests. Signed-off-by: mesilov --- Makefile | 5 ++++- tests/Unit/Application/ApplicationStatusTest.php | 2 +- tests/Unit/Core/Credentials/ApplicationProfileTest.php | 2 +- tests/Unit/Core/Credentials/CredentialsTest.php | 2 +- tests/Unit/Core/Response/DTO/TimeTest.php | 2 +- .../HttpClient/RequestId/DefaultRequestIdGeneratorTest.php | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index ee1097e7..6e31904d 100644 --- a/Makefile +++ b/Makefile @@ -3,4 +3,7 @@ default: @egrep -e '^\S+' ./Makefile | grep -v default | sed -r 's/://' | sed -r 's/^/ - /' phpstan: - vendor/bin/phpstan analyse \ No newline at end of file + vendor/bin/phpstan analyse + +test-unit: + vendor/bin/phpunit --testsuite unit_tests \ No newline at end of file diff --git a/tests/Unit/Application/ApplicationStatusTest.php b/tests/Unit/Application/ApplicationStatusTest.php index d768b0e4..ba6214ce 100644 --- a/tests/Unit/Application/ApplicationStatusTest.php +++ b/tests/Unit/Application/ApplicationStatusTest.php @@ -49,7 +49,7 @@ public function testInitFromString(): void /** * @return \Generator */ - public function statusDataProvider(): Generator + public static function statusDataProvider(): Generator { yield 'free' => [ 'F', diff --git a/tests/Unit/Core/Credentials/ApplicationProfileTest.php b/tests/Unit/Core/Credentials/ApplicationProfileTest.php index 17956aac..27d415f7 100644 --- a/tests/Unit/Core/Credentials/ApplicationProfileTest.php +++ b/tests/Unit/Core/Credentials/ApplicationProfileTest.php @@ -31,7 +31,7 @@ public function testFromArray(array $arr, ?string $expectedException): void $this->assertEquals($prof->getClientSecret(), $arr['BITRIX24_PHP_SDK_APPLICATION_CLIENT_SECRET']); } - public function arrayDataProvider(): Generator + public static function arrayDataProvider(): Generator { yield 'valid' => [ [ diff --git a/tests/Unit/Core/Credentials/CredentialsTest.php b/tests/Unit/Core/Credentials/CredentialsTest.php index 5d3ccfdb..64438eef 100644 --- a/tests/Unit/Core/Credentials/CredentialsTest.php +++ b/tests/Unit/Core/Credentials/CredentialsTest.php @@ -70,7 +70,7 @@ public function testDomainUrlWithProtocol(): void * @throws \Bitrix24\SDK\Core\Exceptions\InvalidArgumentException * @throws \Bitrix24\SDK\Core\Exceptions\UnknownScopeCodeException */ - public function credentialsDataProviderWithDomainUrlVariants(): Generator + public static function credentialsDataProviderWithDomainUrlVariants(): Generator { yield 'with webhook walid domain url' => [ Credentials::createFromWebhook(new WebhookUrl('https://bitrix24-php-sdk-playground.bitrix24.ru/rest/1/valid-webhook/')), diff --git a/tests/Unit/Core/Response/DTO/TimeTest.php b/tests/Unit/Core/Response/DTO/TimeTest.php index 64f597b2..99a7db9d 100644 --- a/tests/Unit/Core/Response/DTO/TimeTest.php +++ b/tests/Unit/Core/Response/DTO/TimeTest.php @@ -36,7 +36,7 @@ public function testInitFromResponseData(array $result): void /** * @return \Generator */ - public function timingsDataProvider(): Generator + public static function timingsDataProvider(): Generator { yield 'without operating reset at' => [ [ diff --git a/tests/Unit/Infrastructure/HttpClient/RequestId/DefaultRequestIdGeneratorTest.php b/tests/Unit/Infrastructure/HttpClient/RequestId/DefaultRequestIdGeneratorTest.php index fab333b8..48b21e52 100644 --- a/tests/Unit/Infrastructure/HttpClient/RequestId/DefaultRequestIdGeneratorTest.php +++ b/tests/Unit/Infrastructure/HttpClient/RequestId/DefaultRequestIdGeneratorTest.php @@ -26,7 +26,7 @@ public function testExistsRequestId($requestIdKey, $requestId): void unset($_SERVER[$requestIdKey]); } - public function requestIdKeyDataProvider(): Generator + public static function requestIdKeyDataProvider(): Generator { yield 'REQUEST_ID' => [ 'REQUEST_ID', From 6aeacd6f96fd3edf0bdd3471b569d9b5d2d0b82c Mon Sep 17 00:00:00 2001 From: mesilov Date: Sun, 18 Feb 2024 23:13:29 +0600 Subject: [PATCH 91/94] Refactor constants declaration in DefaultRequestIdGenerator Constants in DefaultRequestIdGenerator were refactored to remove 'string' and 'array' type declarations. These changes conform to PHP constants declaration rules, which do not require type specification. Signed-off-by: mesilov --- .../HttpClient/RequestId/DefaultRequestIdGenerator.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php b/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php index 9b49016d..84defcd0 100644 --- a/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php +++ b/src/Infrastructure/HttpClient/RequestId/DefaultRequestIdGenerator.php @@ -8,9 +8,9 @@ class DefaultRequestIdGenerator implements RequestIdGeneratorInterface { - private const string DEFAULT_REQUEST_ID_HEADER_FIELD_NAME = 'X-Request-ID'; - private const string DEFAULT_QUERY_STRING_PARAMETER_NAME = 'bx24_request_id'; - private const array KEY_NAME_VARIANTS = [ + private const DEFAULT_REQUEST_ID_HEADER_FIELD_NAME = 'X-Request-ID'; + private const DEFAULT_QUERY_STRING_PARAMETER_NAME = 'bx24_request_id'; + private const KEY_NAME_VARIANTS = [ 'REQUEST_ID', 'HTTP_X_REQUEST_ID', 'UNIQUE_ID' From 86f088537301f2190907c09acfbad3cd6e670ae9 Mon Sep 17 00:00:00 2001 From: mesilov Date: Sun, 18 Feb 2024 23:15:39 +0600 Subject: [PATCH 92/94] Update constant declaration in AbstractCrmItem The constant CRM_USERFIELD_PREFIX in the AbstractCrmItem class has been updated to remove the 'string' type declaration, in accordance with PHP's constants declaration rules which do not require specifying the type. Signed-off-by: mesilov --- src/Services/CRM/Common/Result/AbstractCrmItem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Services/CRM/Common/Result/AbstractCrmItem.php b/src/Services/CRM/Common/Result/AbstractCrmItem.php index f7d9c321..84ffb151 100644 --- a/src/Services/CRM/Common/Result/AbstractCrmItem.php +++ b/src/Services/CRM/Common/Result/AbstractCrmItem.php @@ -17,7 +17,7 @@ class AbstractCrmItem extends AbstractItem { - private const string CRM_USERFIELD_PREFIX = 'UF_CRM_'; + private const CRM_USERFIELD_PREFIX = 'UF_CRM_'; /** * @var Currency From 855cd960b7193e19c2df05326cb980f0d1152323 Mon Sep 17 00:00:00 2001 From: mesilov Date: Sun, 18 Feb 2024 23:17:22 +0600 Subject: [PATCH 93/94] Update constant declaration in AbstractCatalogItem The constant CRM_USERFIELD_PREFIX in the AbstractCatalogItem class has been updated to remove the 'string' type declaration, keeping in line with PHP's convention for declaring constants which does not require specifying the type. Signed-off-by: mesilov --- src/Services/Catalog/Common/Result/AbstractCatalogItem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Services/Catalog/Common/Result/AbstractCatalogItem.php b/src/Services/Catalog/Common/Result/AbstractCatalogItem.php index 435beeb4..b1d0138a 100644 --- a/src/Services/Catalog/Common/Result/AbstractCatalogItem.php +++ b/src/Services/Catalog/Common/Result/AbstractCatalogItem.php @@ -13,7 +13,7 @@ abstract class AbstractCatalogItem extends AbstractItem { - private const string CRM_USERFIELD_PREFIX = 'UF_CRM_'; + private const CRM_USERFIELD_PREFIX = 'UF_CRM_'; /** * @var Currency From bad9929eae5d3df471cdfdcf4c4ca05603c8f5b7 Mon Sep 17 00:00:00 2001 From: mesilov Date: Sun, 18 Feb 2024 23:19:58 +0600 Subject: [PATCH 94/94] Update release date and php support in CHANGELOG The release date for version 2.0-beta.1 in the CHANGELOG.md file has been updated. Additionally, the PHP support information has also been changed to reflect the added support for PHP 8.2 and 8.3, and the removal of support for PHP 8.0 and 8.1. Signed-off-by: mesilov --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e31e28c0..7c8ad878 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,10 @@ # bitrix24-php-sdk change log -## 2.0-beta.1 — 25.03.2023 +## 2.0-beta.1 — 18.02.2024 ### Added -* ❗️add php 8.3 support, drop 8.1 and 8.0 support +* ❗️add php 8.3, 8.2 support, drop 8.1 and 8.0 support * add `Symfony\Component\Uid\Uuid` requirements * add contracts for bitrix24 applications based on bitrix24-php-sdk - `Bitrix24\SDK\Application\Contracts`, now added `Bitrix24Account`