diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index fb9a9be7..9073564e 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -6,6 +6,7 @@ $finder = Finder::create() ->in(__DIR__ . '/src/Infrastructure/Console/Commands/') + ->in(__DIR__ . '/src/Services/CRM/Quote/') ->in(__DIR__ . '/src/Services/CRM/Lead/') ->in(__DIR__ . '/src/Services/CRM/Currency/') ->name('*.php') diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a9bd963..be7371e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,39 @@ ### Added +- Added service `Services\CRM\Quote\Service\Quote` with support methods, + see [crm.quote.* methods](https://github.com/bitrix24/b24phpsdk/issues/179): + - `fields` returns a list of fields for the quote + - `get` returns the settings of the quote by Id + - `list` returns a list of quote + - `add` creates a new quote + - `delete` deletes a quote + - `update` modifies the quote + - `countByFilter` count quotes by filter +- Added support for events: + - `OnCrmQuoteAdd` + - `OnCrmQuoteDelete` + - `OnCrmQuoteUpdate` + - `OnCrmQuoteUserFieldAdd` + - `OnCrmQuoteUserFieldDelete` + - `OnCrmQuoteUserFieldSetEnumValues` + - `OnCrmQuoteUserFieldUpdate` +- Added service `Services\CRM\Quote\Service\QuoteUserfield` with support methods: + - `add` add userfield to a quote + - `get` get userfield to a quote + - `list` list userfields + - `delete` delete userfield + - `update` update userfield +- Added service `Services\CRM\Quote\Service\QuoteProductRows` with support methods: + - `set` Adds products to a quote + - `get` Returns the products of a quote +- Added service `Services\CRM\Quote\Service\QuoteContact` with support methods, + - `fields` get fiels for quote contact connection + - `setItems` set contacts related with quote + - `get` get contacts related to quote + - `deleteItems` delete all relations for quote + - `add` add contact relation with quote + - `delete` delete contact relation with quote - Added service `CRM\Lead\Service\LeadUserfield` with support methods, see [add crm.lead.userfield.* methods](https://github.com/bitrix24/b24phpsdk/issues/177): - `add` add userfield to lead diff --git a/Makefile b/Makefile index aa9ae33c..50b45041 100644 --- a/Makefile +++ b/Makefile @@ -195,9 +195,9 @@ integration_tests_scope_crm_currency: integration_tests_deal_recurring: docker-compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_deal_recurring -.PHONY: integration_tests_scope_automation -integration_tests_scope_automation: - docker-compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_scope_automation +.PHONY: integration_tests_crm_quote +integration_tests_crm_quote: + docker-compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_crm_quote # work dev environment .PHONY: php-dev-server-up diff --git a/phpstan.neon.dist b/phpstan.neon.dist index fdc9e194..10e8babd 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -11,6 +11,11 @@ parameters: - tests/Integration/Services/IMOpenLines - tests/Integration/Services/Main - tests/Integration/Services/Placement + - tests/Integration/Services/CRM/Quote/Service/QuoteTest.php + - tests/Integration/Services/CRM/Quote/Service/BatchTest.php + - tests/Integration/Services/CRM/Quote/Service/QuoteContactTest.php + - tests/Integration/Services/CRM/Quote/Service/QuoteUserfieldTest.php + - tests/Integration/Services/CRM/Quote/Service/QuoteProductRowsTest.php - tests/Integration/Services/CRM/Lead/Service/LeadUserfieldTest.php - tests/Integration/Services/CRM/Currency bootstrapFiles: diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 2a703dce..aa8ba6a7 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -58,6 +58,9 @@ ./tests/Integration/Services/CRM/Automation/ + + ./tests/Integration/Services/CRM/Quote/ + diff --git a/rector.php b/rector.php index 5fa6d616..8509a1de 100644 --- a/rector.php +++ b/rector.php @@ -36,6 +36,8 @@ __DIR__ . '/tests/Integration/Services/Main', __DIR__ . '/src/Services/Placement', __DIR__ . '/tests/Integration/Services/Placement', + __DIR__ . '/src/Services/CRM/Quote', + __DIR__ . '/tests/Integration/Services/CRM/Quote/Service', __DIR__ . '/src/Services/CRM/Currency', __DIR__ . '/tests/Integration/Services/CRM/Currency', __DIR__ . '/tests/Unit/', diff --git a/src/Services/CRM/CRMServiceBuilder.php b/src/Services/CRM/CRMServiceBuilder.php index f2505d40..42f77abd 100644 --- a/src/Services/CRM/CRMServiceBuilder.php +++ b/src/Services/CRM/CRMServiceBuilder.php @@ -311,7 +311,7 @@ public function lead(): Lead\Service\Lead return $this->serviceCache[__METHOD__]; } - + public function leadUserfield(): Lead\Service\LeadUserfield { if (!isset($this->serviceCache[__METHOD__])) { @@ -324,6 +324,56 @@ public function leadUserfield(): Lead\Service\LeadUserfield return $this->serviceCache[__METHOD__]; } + + public function quote(): Quote\Service\Quote + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Quote\Service\Quote( + new Quote\Service\Batch($this->batch, $this->log), + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } + + public function quoteContact(): Quote\Service\QuoteContact + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Quote\Service\QuoteContact( + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } + + /** + * @return Quote\Service\QuoteProductRows + */ + public function quoteProductRows(): Quote\Service\QuoteProductRows + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Quote\Service\QuoteProductRows($this->core, $this->log); + } + + return $this->serviceCache[__METHOD__]; + } + + public function quoteUserfield(): Quote\Service\QuoteUserfield + { + if (!isset($this->serviceCache[__METHOD__])) { + $this->serviceCache[__METHOD__] = new Quote\Service\QuoteUserfield( + new UserfieldConstraints(), + $this->core, + $this->log + ); + } + + return $this->serviceCache[__METHOD__]; + } public function activity(): Activity\Service\Activity { diff --git a/src/Services/CRM/Quote/Events/CrmQuoteEventsFactory.php b/src/Services/CRM/Quote/Events/CrmQuoteEventsFactory.php new file mode 100644 index 00000000..97c67361 --- /dev/null +++ b/src/Services/CRM/Quote/Events/CrmQuoteEventsFactory.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Events; + +use Bitrix24\SDK\Core\Contracts\Events\EventInterface; +use Bitrix24\SDK\Core\Contracts\Events\EventsFabricInterface; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteAdd\OnCrmQuoteAdd; +use Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteDelete\OnCrmQuoteDelete; +use Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteUpdate\OnCrmQuoteUpdate; +use Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteUserFieldAdd\OnCrmQuoteUserFieldAdd; +use Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteUserFieldDelete\OnCrmQuoteUserFieldDelete; +use Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteUserFieldSetEnumValues\OnCrmQuoteUserFieldSetEnumValues; +use Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteUserFieldUpdate\OnCrmQuoteUserFieldUpdate; +use Symfony\Component\HttpFoundation\Request; + +readonly class CrmQuoteEventsFactory implements EventsFabricInterface +{ + public function isSupport(string $eventCode): bool + { + return in_array(strtoupper($eventCode), [ + OnCrmQuoteAdd::CODE, + OnCrmQuoteUpdate::CODE, + OnCrmQuoteDelete::CODE, + OnCrmQuoteUserFieldAdd::CODE, + OnCrmQuoteUserFieldUpdate::CODE, + OnCrmQuoteUserFieldDelete::CODE, + OnCrmQuoteUserFieldSetEnumValues::CODE, + ], true); + } + + /** + * @throws InvalidArgumentException + */ + public function create(Request $eventRequest): EventInterface + { + $eventPayload = $eventRequest->request->all(); + if (!array_key_exists('event', $eventPayload)) { + throw new InvalidArgumentException('«event» key not found in event payload'); + } + + return match ($eventPayload['event']) { + OnCrmQuoteAdd::CODE => new OnCrmQuoteAdd($eventRequest), + OnCrmQuoteUpdate::CODE => new OnCrmQuoteUpdate($eventRequest), + OnCrmQuoteDelete::CODE => new OnCrmQuoteDelete($eventRequest), + OnCrmQuoteUserFieldAdd::CODE => new OnCrmQuoteUserFieldAdd($eventRequest), + OnCrmQuoteUserFieldUpdate::CODE => new OnCrmQuoteUserFieldUpdate($eventRequest), + OnCrmQuoteUserFieldDelete::CODE => new OnCrmQuoteUserFieldDelete($eventRequest), + OnCrmQuoteUserFieldSetEnumValues::CODE => new OnCrmQuoteUserFieldSetEnumValues($eventRequest), + default => throw new InvalidArgumentException( + sprintf('Unexpected event code «%s»', $eventPayload['event']) + ), + }; + } +} diff --git a/src/Services/CRM/Quote/Events/OnCrmQuoteAdd/OnCrmQuoteAdd.php b/src/Services/CRM/Quote/Events/OnCrmQuoteAdd/OnCrmQuoteAdd.php new file mode 100644 index 00000000..baa91311 --- /dev/null +++ b/src/Services/CRM/Quote/Events/OnCrmQuoteAdd/OnCrmQuoteAdd.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteAdd; + +use Bitrix24\SDK\Application\Requests\Events\AbstractEventRequest; + +class OnCrmQuoteAdd extends AbstractEventRequest +{ + public const CODE = 'ONCRMQUOTEADD'; + + public function getPayload(): OnCrmQuoteAddPayload + { + return new OnCrmQuoteAddPayload($this->eventPayload['data']); + } +} diff --git a/src/Services/CRM/Quote/Events/OnCrmQuoteAdd/OnCrmQuoteAddPayload.php b/src/Services/CRM/Quote/Events/OnCrmQuoteAdd/OnCrmQuoteAddPayload.php new file mode 100644 index 00000000..1a7ce1ad --- /dev/null +++ b/src/Services/CRM/Quote/Events/OnCrmQuoteAdd/OnCrmQuoteAddPayload.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteAdd; + +use Bitrix24\SDK\Core\Result\AbstractItem; + +/** + * @property-read array{ + * ID: int, + * } $FIELDS + */ +class OnCrmQuoteAddPayload extends AbstractItem +{ +} diff --git a/src/Services/CRM/Quote/Events/OnCrmQuoteDelete/OnCrmQuoteDelete.php b/src/Services/CRM/Quote/Events/OnCrmQuoteDelete/OnCrmQuoteDelete.php new file mode 100644 index 00000000..d12294b0 --- /dev/null +++ b/src/Services/CRM/Quote/Events/OnCrmQuoteDelete/OnCrmQuoteDelete.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteDelete; + +use Bitrix24\SDK\Application\Requests\Events\AbstractEventRequest; + +class OnCrmQuoteDelete extends AbstractEventRequest +{ + public const CODE = 'ONCRMQUOTEDELETE'; + + public function getPayload(): OnCrmQuoteDeletePayload + { + return new OnCrmQuoteDeletePayload($this->eventPayload['data']); + } +} diff --git a/src/Services/CRM/Quote/Events/OnCrmQuoteDelete/OnCrmQuoteDeletePayload.php b/src/Services/CRM/Quote/Events/OnCrmQuoteDelete/OnCrmQuoteDeletePayload.php new file mode 100644 index 00000000..74249ca1 --- /dev/null +++ b/src/Services/CRM/Quote/Events/OnCrmQuoteDelete/OnCrmQuoteDeletePayload.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteDelete; + +use Bitrix24\SDK\Core\Result\AbstractItem; + +/** + * @property-read array{ + * ID: int, + * } $FIELDS + */ +class OnCrmQuoteDeletePayload extends AbstractItem +{ +} diff --git a/src/Services/CRM/Quote/Events/OnCrmQuoteUpdate/OnCrmQuoteUpdate.php b/src/Services/CRM/Quote/Events/OnCrmQuoteUpdate/OnCrmQuoteUpdate.php new file mode 100644 index 00000000..aab0d122 --- /dev/null +++ b/src/Services/CRM/Quote/Events/OnCrmQuoteUpdate/OnCrmQuoteUpdate.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteUpdate; + +use Bitrix24\SDK\Application\Requests\Events\AbstractEventRequest; + +class OnCrmQuoteUpdate extends AbstractEventRequest +{ + public const CODE = 'ONCRMQUOTEUPDATE'; + + public function getPayload(): OnCrmQuoteUpdatePayload + { + return new OnCrmQuoteUpdatePayload($this->eventPayload['data']); + } +} diff --git a/src/Services/CRM/Quote/Events/OnCrmQuoteUpdate/OnCrmQuoteUpdatePayload.php b/src/Services/CRM/Quote/Events/OnCrmQuoteUpdate/OnCrmQuoteUpdatePayload.php new file mode 100644 index 00000000..3c34977a --- /dev/null +++ b/src/Services/CRM/Quote/Events/OnCrmQuoteUpdate/OnCrmQuoteUpdatePayload.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteUpdate; + +use Bitrix24\SDK\Core\Result\AbstractItem; + +/** + * @property-read array{ + * ID: int, + * RECURRING_DEAL_ID: int, + * } $FIELDS + */ +class OnCrmQuoteUpdatePayload extends AbstractItem +{ +} diff --git a/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldAdd/OnCrmQuoteUserFieldAdd.php b/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldAdd/OnCrmQuoteUserFieldAdd.php new file mode 100644 index 00000000..9c849b12 --- /dev/null +++ b/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldAdd/OnCrmQuoteUserFieldAdd.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteUserFieldAdd; + +use Bitrix24\SDK\Application\Requests\Events\AbstractEventRequest; + +class OnCrmQuoteUserFieldAdd extends AbstractEventRequest +{ + public const CODE = 'ONCRMQUOTEUSERFIELDADD'; + + public function getPayload(): OnCrmQuoteUserFieldAddPayload + { + return new OnCrmQuoteUserFieldAddPayload($this->eventPayload['data']); + } +} diff --git a/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldAdd/OnCrmQuoteUserFieldAddPayload.php b/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldAdd/OnCrmQuoteUserFieldAddPayload.php new file mode 100644 index 00000000..d56bf4fa --- /dev/null +++ b/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldAdd/OnCrmQuoteUserFieldAddPayload.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteUserFieldAdd; + +use Bitrix24\SDK\Core\Result\AbstractItem; +use Bitrix24\SDK\Services\Telephony\Common\CrmEntityType; + +/** + * @property-read positive-int $id + * @property-read non-empty-string $entityId + * @property-read non-empty-string $fieldName + */ +class OnCrmQuoteUserFieldAddPayload extends AbstractItem +{ +} diff --git a/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldDelete/OnCrmQuoteUserFieldDelete.php b/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldDelete/OnCrmQuoteUserFieldDelete.php new file mode 100644 index 00000000..42280462 --- /dev/null +++ b/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldDelete/OnCrmQuoteUserFieldDelete.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteUserFieldDelete; + +use Bitrix24\SDK\Application\Requests\Events\AbstractEventRequest; + +class OnCrmQuoteUserFieldDelete extends AbstractEventRequest +{ + public const CODE = 'ONCRMQUOTEUSERFIELDDELETE'; + + public function getPayload(): OnCrmQuoteUserFieldDeletePayload + { + return new OnCrmQuoteUserFieldDeletePayload($this->eventPayload['data']); + } +} diff --git a/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldDelete/OnCrmQuoteUserFieldDeletePayload.php b/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldDelete/OnCrmQuoteUserFieldDeletePayload.php new file mode 100644 index 00000000..0650741c --- /dev/null +++ b/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldDelete/OnCrmQuoteUserFieldDeletePayload.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteUserFieldDelete; + +use Bitrix24\SDK\Core\Result\AbstractItem; +use Bitrix24\SDK\Services\Telephony\Common\CrmEntityType; + +/** + * @property-read positive-int $id + * @property-read non-empty-string $entityId + * @property-read non-empty-string $fieldName + */ +class OnCrmQuoteUserFieldDeletePayload extends AbstractItem +{ +} diff --git a/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldSetEnumValues/OnCrmQuoteUserFieldSetEnumValues.php b/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldSetEnumValues/OnCrmQuoteUserFieldSetEnumValues.php new file mode 100644 index 00000000..a8ec9e53 --- /dev/null +++ b/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldSetEnumValues/OnCrmQuoteUserFieldSetEnumValues.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteUserFieldSetEnumValues; + +use Bitrix24\SDK\Application\Requests\Events\AbstractEventRequest; + +class OnCrmQuoteUserFieldSetEnumValues extends AbstractEventRequest +{ + public const CODE = 'ONCRMQUOTEUSERFIELDSETENUMVALUES'; + + public function getPayload(): OnCrmQuoteUserFieldSetEnumValuesPayload + { + return new OnCrmQuoteUserFieldSetEnumValuesPayload($this->eventPayload['data']); + } +} diff --git a/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldSetEnumValues/OnCrmQuoteUserFieldSetEnumValuesPayload.php b/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldSetEnumValues/OnCrmQuoteUserFieldSetEnumValuesPayload.php new file mode 100644 index 00000000..b366c5ee --- /dev/null +++ b/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldSetEnumValues/OnCrmQuoteUserFieldSetEnumValuesPayload.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteUserFieldSetEnumValues; + +use Bitrix24\SDK\Core\Result\AbstractItem; +use Bitrix24\SDK\Services\Telephony\Common\CrmEntityType; + +/** + * @property-read positive-int $id + * @property-read non-empty-string $entityId + * @property-read non-empty-string $fieldName + */ +class OnCrmQuoteUserFieldSetEnumValuesPayload extends AbstractItem +{ +} diff --git a/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldUpdate/OnCrmQuoteUserFieldUpdate.php b/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldUpdate/OnCrmQuoteUserFieldUpdate.php new file mode 100644 index 00000000..fcdb5130 --- /dev/null +++ b/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldUpdate/OnCrmQuoteUserFieldUpdate.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteUserFieldUpdate; + +use Bitrix24\SDK\Application\Requests\Events\AbstractEventRequest; + +class OnCrmQuoteUserFieldUpdate extends AbstractEventRequest +{ + public const CODE = 'ONCRMQUOTEUSERFIELDUPDATE'; + + public function getPayload(): OnCrmQuoteUserFieldUpdatePayload + { + return new OnCrmQuoteUserFieldUpdatePayload($this->eventPayload['data']); + } +} diff --git a/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldUpdate/OnCrmQuoteUserFieldUpdatePayload.php b/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldUpdate/OnCrmQuoteUserFieldUpdatePayload.php new file mode 100644 index 00000000..05a71a68 --- /dev/null +++ b/src/Services/CRM/Quote/Events/OnCrmQuoteUserFieldUpdate/OnCrmQuoteUserFieldUpdatePayload.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Events\OnCrmQuoteUserFieldUpdate; + +use Bitrix24\SDK\Core\Result\AbstractItem; +use Bitrix24\SDK\Services\Telephony\Common\CrmEntityType; + +/** + * @property-read positive-int $id + * @property-read non-empty-string $entityId + * @property-read non-empty-string $fieldName + */ +class OnCrmQuoteUserFieldUpdatePayload extends AbstractItem +{ +} diff --git a/src/Services/CRM/Quote/Result/QuoteContactConnectionItemResult.php b/src/Services/CRM/Quote/Result/QuoteContactConnectionItemResult.php new file mode 100644 index 00000000..12b20524 --- /dev/null +++ b/src/Services/CRM/Quote/Result/QuoteContactConnectionItemResult.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Result; + +use Bitrix24\SDK\Services\CRM\Common\Result\AbstractCrmItem; + +/** + * @property-read int $CONTACT_ID + * @property-read int $SORT + * @property-read bool $IS_PRIMARY + */ +class QuoteContactConnectionItemResult extends AbstractCrmItem +{ +} diff --git a/src/Services/CRM/Quote/Result/QuoteContactConnectionResult.php b/src/Services/CRM/Quote/Result/QuoteContactConnectionResult.php new file mode 100644 index 00000000..3af3cd83 --- /dev/null +++ b/src/Services/CRM/Quote/Result/QuoteContactConnectionResult.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class QuoteContactConnectionResult extends AbstractResult +{ + /** + * @return QuoteContactConnectionItemResult[] + * @throws BaseException + */ + public function getContactConnections(): array + { + $res = []; + foreach ($this->getCoreResponse()->getResponseData()->getResult() as $item) { + $res[] = new QuoteContactConnectionItemResult($item); + } + + return $res; + } +} diff --git a/src/Services/CRM/Quote/Result/QuoteItemResult.php b/src/Services/CRM/Quote/Result/QuoteItemResult.php new file mode 100644 index 00000000..9475b36f --- /dev/null +++ b/src/Services/CRM/Quote/Result/QuoteItemResult.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Result; + +use Bitrix24\SDK\Services\CRM\Common\Result\AbstractCrmItem; +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\Website; +use Carbon\CarbonImmutable; +use Money\Currency; +use Money\Money; + +/** + * Class QuoteItemResult + * + * @property-read int $ID + * @property-read string $TITLE + * @property-read int|null $ASSIGNED_BY_ID + * @property-read int|null $LAST_ACTIVITY_BY + * @property-read CarbonImmutable|null $LAST_ACTIVITY_TIME + * @property-read string|null $LAST_COMMUNICATION_TIME + * @property-read CarbonImmutable|null $BEGINDATE + * @property-read CarbonImmutable|null $CLOSEDATE + * @property-read CarbonImmutable|null $ACTUAL_DATE + * @property-read string|null $CLIENT_ADDR + * @property-read string|null $CLIENT_CONTACT + * @property-read string|null $CLIENT_EMAIL + * @property-read string|null $CLIENT_PHONE + * @property-read string|null $CLIENT_TITLE + * @property-read string|null $CLIENT_TPA_ID + * @property-read string|null $CLIENT_TP_ID + * @property-read bool|null $CLOSED + * @property-read string|null $COMMENTS + * @property-read int|null $COMPANY_ID + * @property-read int|null $CONTACT_ID + * @property-read array|null $CONTACT_IDS + * @property-read string|null $CONTENT + * @property-read int|null $CREATED_BY_ID + * @property-read Currency|null $CURRENCY_ID + * @property-read CarbonImmutable|null $DATE_CREATE + * @property-read CarbonImmutable|null $DATE_MODIFY + * @property-read int|null $DEAL_ID + * @property-read int|null $LEAD_ID + * @property-read int|null $LOCATION_ID + * @property-read int|null $MODIFY_BY_ID + * @property-read int|null $MYCOMPANY_ID + * @property-read bool|null $OPENED + * @property-read Money|null $OPPORTUNITY + * @property-read int|null $PERSON_TYPE_ID + * @property-read string|null $QUOTE_NUMBER + * @property-read string|null $STATUS_ID + * @property-read Money|null $TAX_VALUE + * @property-read string|null $TERMS + * @property-read string|null $UTM_SOURCE + * @property-read string|null $UTM_MEDIUM + * @property-read string|null $UTM_CAMPAIGN + * @property-read string|null $UTM_CONTENT + * @property-read string|null $UTM_TERM + */ +class QuoteItemResult extends AbstractCrmItem +{ + /** + * + * @return mixed|null + * @throws \Bitrix24\SDK\Services\CRM\Userfield\Exceptions\UserfieldNotFoundException + */ + public function getUserfieldByFieldName(string $userfieldName) + { + return $this->getKeyWithUserfieldByFieldName($userfieldName); + } +} diff --git a/src/Services/CRM/Quote/Result/QuoteProductRowItemResult.php b/src/Services/CRM/Quote/Result/QuoteProductRowItemResult.php new file mode 100644 index 00000000..e0eb1897 --- /dev/null +++ b/src/Services/CRM/Quote/Result/QuoteProductRowItemResult.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Result; + +use Money\Money; +use MoneyPHP\Percentage\Percentage; +use Bitrix24\SDK\Services\CRM\Common\Result\DiscountType; +use Carbon\CarbonImmutable; +use Bitrix24\SDK\Services\CRM\Common\Result\AbstractCrmItem; + +/** + * @property-read int $ID + * @property-read int $OWNER_ID + * @property-read string $OWNER_TYPE + * @property-read int $PRODUCT_ID + * @property-read string $PRODUCT_NAME + * @property-read string|null $ORIGINAL_PRODUCT_NAME + * @property-read string|null $PRODUCT_DESCRIPTION + * @property-read Money $PRICE price with taxes and discounts + * @property-read Money $PRICE_EXCLUSIVE without taxes but with discounts + * @property-read Money $PRICE_NETTO without taxes and discounts + * @property-read Money $PRICE_BRUTTO without discounts but with taxes + * @property-read Money $PRICE_ACCOUNT formatted price + * @property-read string $QUANTITY + * @property-read DiscountType $DISCOUNT_TYPE_ID + * @property-read Percentage $DISCOUNT_RATE + * @property-read Money $DISCOUNT_SUM + * @property-read string $TAX_RATE + * @property-read bool $TAX_INCLUDED + * @property-read string $CUSTOMIZED + * @property-read int $MEASURE_CODE + * @property-read string $MEASURE_NAME + * @property-read int $SORT + * @property-read string|null $XML_ID + * @property-read int $TYPE + * @property-read int|null $STORE_ID + * @property-read int|null $RESERVE_ID + * @property-read CarbonImmutable|null $DATE_RESERVE_END + * @property-read int|null $RESERVE_QUANTITY + */ +class QuoteProductRowItemResult extends AbstractCrmItem +{ +} diff --git a/src/Services/CRM/Quote/Result/QuoteProductRowItemsResult.php b/src/Services/CRM/Quote/Result/QuoteProductRowItemsResult.php new file mode 100644 index 00000000..b7412d5a --- /dev/null +++ b/src/Services/CRM/Quote/Result/QuoteProductRowItemsResult.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Response\Response; +use Bitrix24\SDK\Core\Result\AbstractResult; +use Money\Currency; + +/** + * Class QuoteProductRowItemsResult + * + * @package Bitrix24\SDK\Services\CRM\Quote\Result + */ +class QuoteProductRowItemsResult extends AbstractResult +{ + public function __construct(Response $coreResponse, private readonly Currency $currency) + { + parent::__construct($coreResponse); + } + + /** + * @return QuoteProductRowItemResult[] + * @throws BaseException + */ + public function getProductRows(): array + { + $res = []; + if (!empty($this->getCoreResponse()->getResponseData()->getResult()['result']['rows'])) { + foreach ($this->getCoreResponse()->getResponseData()->getResult()['result']['rows'] as $productRow) { + $res[] = new QuoteProductRowItemResult($productRow, $this->currency); + } + } else { + foreach ($this->getCoreResponse()->getResponseData()->getResult() as $productRow) { + $res[] = new QuoteProductRowItemResult($productRow, $this->currency); + } + } + + return $res; + } +} diff --git a/src/Services/CRM/Quote/Result/QuoteResult.php b/src/Services/CRM/Quote/Result/QuoteResult.php new file mode 100644 index 00000000..1cf4b9af --- /dev/null +++ b/src/Services/CRM/Quote/Result/QuoteResult.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Result; + +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class QuoteResult + * + * @package Bitrix24\SDK\Services\CRM\Quote\Result + */ +class QuoteResult extends AbstractResult +{ + /** + * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + */ + public function quote(): QuoteItemResult + { + return new QuoteItemResult($this->getCoreResponse()->getResponseData()->getResult()); + } +} diff --git a/src/Services/CRM/Quote/Result/QuoteUserfieldItemResult.php b/src/Services/CRM/Quote/Result/QuoteUserfieldItemResult.php new file mode 100644 index 00000000..9b8f0301 --- /dev/null +++ b/src/Services/CRM/Quote/Result/QuoteUserfieldItemResult.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Result; + +use Bitrix24\SDK\Services\CRM\Userfield\Result\AbstractUserfieldItemResult; + +class QuoteUserfieldItemResult extends AbstractUserfieldItemResult +{ +} diff --git a/src/Services/CRM/Quote/Result/QuoteUserfieldResult.php b/src/Services/CRM/Quote/Result/QuoteUserfieldResult.php new file mode 100644 index 00000000..b4cbe9f9 --- /dev/null +++ b/src/Services/CRM/Quote/Result/QuoteUserfieldResult.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Result; + +use Bitrix24\SDK\Core\Result\AbstractResult; + +class QuoteUserfieldResult extends AbstractResult +{ + /** + * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + */ + public function userfieldItem(): QuoteUserfieldItemResult + { + return new QuoteUserfieldItemResult($this->getCoreResponse()->getResponseData()->getResult()); + } +} diff --git a/src/Services/CRM/Quote/Result/QuoteUserfieldsResult.php b/src/Services/CRM/Quote/Result/QuoteUserfieldsResult.php new file mode 100644 index 00000000..414091f0 --- /dev/null +++ b/src/Services/CRM/Quote/Result/QuoteUserfieldsResult.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +class QuoteUserfieldsResult extends AbstractResult +{ + /** + * @return \Bitrix24\SDK\Services\CRM\Quote\Result\QuoteUserfieldItemResult[] + * @throws BaseException + */ + public function getUserfields(): array + { + $res = []; + foreach ($this->getCoreResponse()->getResponseData()->getResult() as $item) { + $res[] = new QuoteUserfieldItemResult($item); + } + + return $res; + } +} diff --git a/src/Services/CRM/Quote/Result/QuotesResult.php b/src/Services/CRM/Quote/Result/QuotesResult.php new file mode 100644 index 00000000..f676dade --- /dev/null +++ b/src/Services/CRM/Quote/Result/QuotesResult.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Result; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AbstractResult; + +/** + * Class QuotesResult + * + * @package Bitrix24\SDK\Services\CRM\Quote\Result + */ +class QuotesResult extends AbstractResult +{ + /** + * @return QuoteItemResult[] + * @throws BaseException + */ + public function getQuotes(): array + { + $items = []; + foreach ($this->getCoreResponse()->getResponseData()->getResult() as $item) { + $items[] = new QuoteItemResult($item); + } + + return $items; + } +} diff --git a/src/Services/CRM/Quote/Service/Batch.php b/src/Services/CRM/Quote/Service/Batch.php new file mode 100644 index 00000000..59496c2a --- /dev/null +++ b/src/Services/CRM/Quote/Service/Batch.php @@ -0,0 +1,242 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Service; + +use Bitrix24\SDK\Attributes\ApiBatchMethodMetadata; +use Bitrix24\SDK\Attributes\ApiBatchServiceMetadata; +use Bitrix24\SDK\Core\Contracts\BatchOperationsInterface; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Result\AddedItemBatchResult; +use Bitrix24\SDK\Core\Result\DeletedItemBatchResult; +use Bitrix24\SDK\Core\Result\UpdatedItemBatchResult; +use Bitrix24\SDK\Services\CRM\Quote\Result\QuoteItemResult; +use Generator; +use Psr\Log\LoggerInterface; + +#[ApiBatchServiceMetadata(new Scope(['crm']))] +class Batch +{ + /** + * Batch constructor. + */ + public function __construct(protected BatchOperationsInterface $batch, protected LoggerInterface $log) + { + } + + /** + * Batch list method for quotes + * + * @param array{ + * ID?: int, + * ASSIGNED_BY_ID?: int, + * BEGINDATA?: string, + * CLIENT_ADDR?: string, + * CLOSED?: bool, + * CLOSEDATA?: string, + * COMMENTS?: string, + * COMPANY_ID?: int, + * CONTACT_ID?: int, + * CONTACT_IDS?: int[], + * CONTENT?: string, + * CREATED_BY_ID?: int, + * CURRENCY_ID?: string, + * DATE_CREATE?: string, + * DATE_MODIFY?: string, + * DEAL_ID?: int, + * LEAD_ID?: int, + * LOCATION_ID?: int, + * MODIFY_BY_ID?: int, + * MYCOMPANY_ID?: int, + * OPENED?: bool, + * OPPORTUNITY?: string, + * PERSON_TYPE_ID?: int, + * QUOTE_NUMBER?: string, + * STATUS_ID?: string, + * TAX_VALUE?: string, + * TERMS?: string, + * TITLE?: string, + * UTM_CAMPAIGN?: string, + * UTM_CONTENT?: string, + * UTM_MEDIUM?: string, + * UTM_SOURCE?: string, + * UTM_TERM?: string, + * } $order + * + * @param array{ + * ID?: int, + * ASSIGNED_BY_ID?: int, + * BEGINDATA?: string, + * CLIENT_ADDR?: string, + * CLOSED?: bool, + * CLOSEDATA?: string, + * COMMENTS?: string, + * COMPANY_ID?: int, + * CONTACT_ID?: int, + * CONTACT_IDS?: int[], + * CONTENT?: string, + * CREATED_BY_ID?: int, + * CURRENCY_ID?: string, + * DATE_CREATE?: string, + * DATE_MODIFY?: string, + * DEAL_ID?: int, + * LEAD_ID?: int, + * LOCATION_ID?: int, + * MODIFY_BY_ID?: int, + * MYCOMPANY_ID?: int, + * OPENED?: bool, + * OPPORTUNITY?: string, + * PERSON_TYPE_ID?: int, + * QUOTE_NUMBER?: string, + * STATUS_ID?: string, + * TAX_VALUE?: string, + * TERMS?: string, + * TITLE?: string, + * UTM_CAMPAIGN?: string, + * UTM_CONTENT?: string, + * UTM_MEDIUM?: string, + * UTM_SOURCE?: string, + * UTM_TERM?: string, + * } $filter + * @param array $select = ['ID','ASSIGNED_BY_ID','BEGINDATA','CLIENT_ADDR','CLOSED','CLOSEDATA','COMMENTS','COMPANY_ID','CONTACT_ID','CONTACT_IDS','CONTENT','CREATED_BY_ID','CURRENCY_ID','DATE_CREATE','DATE_MODIFY','DEAL_ID','LEAD_ID','LOCATION_ID','MODIFY_BY_ID','MYCOMPANY_ID','OPENED','OPPORTUNITY','PERSON_TYPE_ID','QUOTE_NUMBER','STATUS_ID','TAX_VALUE','TERMS','TITLE','UTM_CAMPAIGN','UTM_CONTENT','UTM_MEDIUM','UTM_SOURCE','UTM_TERM'] + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'crm.quote.list', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-list.html', + 'Batch list method for quotes' + )] + public function list(array $order, array $filter, array $select, ?int $limit = null): Generator + { + $this->log->debug( + 'batchList', + [ + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'limit' => $limit, + ] + ); + foreach ($this->batch->getTraversableList('crm.quote.list', $order, $filter, $select, $limit) as $key => $value) { + yield $key => new QuoteItemResult($value); + } + } + + /** + * Batch adding quotes + * + * @param array $quotes + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'crm.quote.add', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-add.html', + 'Batch adding quotes' + )] + public function add(array $quotes): Generator + { + $items = []; + foreach ($quotes as $quote) { + $items[] = [ + 'fields' => $quote, + ]; + } + + foreach ($this->batch->addEntityItems('crm.quote.add', $items) as $key => $item) { + yield $key => new AddedItemBatchResult($item); + } + } + + /** + * Batch update quotes + * + * Update elements in array with structure + * element_id => [ // quote id + * 'fields' => [] // quote fields to update + * ] + * + * @param array $entityItems + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'crm.quote.update', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-update.html', + 'Update in batch mode a list of quotes' + )] + public function update(array $entityItems): Generator + { + foreach ($this->batch->updateEntityItems('crm.quote.update', $entityItems) as $key => $item) { + yield $key => new UpdatedItemBatchResult($item); + } + } + + /** + * Batch delete quotes + * + * @param int[] $quoteId + * + * @return Generator + * @throws BaseException + */ + #[ApiBatchMethodMetadata( + 'crm.quote.delete', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-delete.html', + 'Batch delete quotes' + )] + public function delete(array $quoteId): Generator + { + foreach ($this->batch->deleteEntityItems('crm.quote.delete', $quoteId) as $key => $item) { + yield $key => new DeletedItemBatchResult($item); + } + } +} diff --git a/src/Services/CRM/Quote/Service/Quote.php b/src/Services/CRM/Quote/Service/Quote.php new file mode 100644 index 00000000..db47bd18 --- /dev/null +++ b/src/Services/CRM/Quote/Service/Quote.php @@ -0,0 +1,317 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Service; + +use Bitrix24\SDK\Attributes\ApiEndpointMetadata; +use Bitrix24\SDK\Attributes\ApiServiceMetadata; +use Bitrix24\SDK\Core\Contracts\CoreInterface; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Core\Result\AddedItemResult; +use Bitrix24\SDK\Core\Result\DeletedItemResult; +use Bitrix24\SDK\Core\Result\FieldsResult; +use Bitrix24\SDK\Core\Result\UpdatedItemResult; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\CRM\Quote\Result\QuoteResult; +use Bitrix24\SDK\Services\CRM\Quote\Result\QuotesResult; +use Psr\Log\LoggerInterface; + +#[ApiServiceMetadata(new Scope(['crm']))] +class Quote extends AbstractService +{ + /** + * Quote constructor. + */ + public function __construct(public Batch $batch, CoreInterface $core, LoggerInterface $logger) + { + parent::__construct($core, $logger); + } + + /** + * add new quote + * + * @link https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-add.html + * + * @param array{ + * ID?: int, + * ASSIGNED_BY_ID?: int, + * LAST_ACTIVITY_BY?: int, + * LAST_ACTIVITY_TIME?: string, + * LAST_COMMUNICATION_TIME?: string, + * BEGINDATE?: string, + * CLOSEDATE?: string, + * ACTUAL_DATE?: string, + * CLIENT_ADDR?: string, + * CLIENT_CONTACT?: string, + * CLIENT_EMAIL?: string, + * CLIENT_PHONE?: string, + * CLIENT_TITLE?: string, + * CLIENT_TPA_ID?: string, + * CLIENT_TP_ID?: string, + * CLOSED?: bool, + * COMMENTS?: string, + * COMPANY_ID?: int, + * CONTACT_ID?: int, + * CONTACT_IDS?: int[], + * CONTENT?: string, + * CREATED_BY_ID?: int, + * CURRENCY_ID?: string, + * DATE_CREATE?: string, + * DATE_MODIFY?: string, + * DEAL_ID?: int, + * LEAD_ID?: int, + * LOCATION_ID?: int, + * MODIFY_BY_ID?: int, + * MYCOMPANY_ID?: int, + * OPENED?: bool, + * OPPORTUNITY?: string, + * PERSON_TYPE_ID?: int, + * QUOTE_NUMBER?: string, + * STATUS_ID?: string, + * TAX_VALUE?: string, + * TERMS?: string, + * TITLE?: string, + * UTM_CAMPAIGN?: string, + * UTM_CONTENT?: string, + * UTM_MEDIUM?: string, + * UTM_SOURCE?: string, + * UTM_TERM?: string, + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'crm.quote.add', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-add.html', + 'Method adds new quote' + )] + public function add(array $fields): AddedItemResult + { + return new AddedItemResult( + $this->core->call( + 'crm.quote.add', + [ + 'fields' => $fields + ] + ) + ); + } + + /** + * Deletes the specified quote and all the associated objects. + * + * @link https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-delete.html + * + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'crm.quote.delete', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-delete.html', + 'Deletes the specified quote and all the associated objects.' + )] + public function delete(int $id): DeletedItemResult + { + return new DeletedItemResult( + $this->core->call( + 'crm.quote.delete', + [ + 'id' => $id, + ] + ) + ); + } + + /** + * Returns the description of the quote fields, including user fields. + * + * @link https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-fields.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'crm.quote.fields', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-fields.html', + 'Returns the description of the quote fields, including user fields.' + )] + public function fields(): FieldsResult + { + return new FieldsResult($this->core->call('crm.quote.fields')); + } + + /** + * Returns a quote by the quote ID. + * + * @link https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-get.html + * + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'crm.quote.get', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-get.html', + 'Returns a quote by the quote ID.' + )] + public function get(int $id): QuoteResult + { + return new QuoteResult($this->core->call('crm.quote.get', ['id' => $id])); + } + + /** + * Get list of quote items. + * + * @link https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-list.html + * + * @param array $order - order of quote items + * @param array $filter - filter array + * @param array $select = ['ID','ASSIGNED_BY_ID','BEGINDATA','CLIENT_ADDR','CLOSED','CLOSEDATA','COMMENTS','COMPANY_ID','CONTACT_ID','CONTACT_IDS','CONTENT','CREATED_BY_ID','CURRENCY_ID','DATE_CREATE','DATE_MODIFY','DEAL_ID','LEAD_ID','LOCATION_ID','MODIFY_BY_ID','MYCOMPANY_ID','OPENED','OPPORTUNITY','PERSON_TYPE_ID','QUOTE_NUMBER','STATUS_ID','TAX_VALUE','TERMS','TITLE','UTM_CAMPAIGN','UTM_CONTENT','UTM_MEDIUM','UTM_SOURCE','UTM_TERM'] + * @param integer $startItem - entity number to start from (usually returned in 'next' field of previous 'crm.quote.list' API call) + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'crm.quote.list', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-list.html', + 'Get list of quote items.' + )] + public function list(array $order, array $filter, array $select, int $startItem = 0): QuotesResult + { + return new QuotesResult( + $this->core->call( + 'crm.quote.list', + [ + 'order' => $order, + 'filter' => $filter, + 'select' => $select, + 'start' => $startItem, + ] + ) + ); + } + + /** + * Updates the specified (existing) quote. + * + * @link https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-update.html + * + * @param array{ + * ID?: int, + * ASSIGNED_BY_ID?: int, + * BEGINDATA?: string, + * CLIENT_ADDR?: string, + * CLOSED?: bool, + * CLOSEDATA?: string, + * COMMENTS?: string, + * COMPANY_ID?: int, + * CONTACT_ID?: int, + * CONTACT_IDS?: int[], + * CONTENT?: string, + * CREATED_BY_ID?: int, + * CURRENCY_ID?: string, + * DATE_CREATE?: string, + * DATE_MODIFY?: string, + * DEAL_ID?: int, + * LEAD_ID?: int, + * LOCATION_ID?: int, + * MODIFY_BY_ID?: int, + * MYCOMPANY_ID?: int, + * OPENED?: bool, + * OPPORTUNITY?: string, + * PERSON_TYPE_ID?: int, + * QUOTE_NUMBER?: string, + * STATUS_ID?: string, + * TAX_VALUE?: string, + * TERMS?: string, + * TITLE?: string, + * UTM_CAMPAIGN?: string, + * UTM_CONTENT?: string, + * UTM_MEDIUM?: string, + * UTM_SOURCE?: string, + * UTM_TERM?: string, + * } $fields + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'crm.quote.update', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-update.html', + 'Updates the specified (existing) quote.' + )] + public function update(int $id, array $fields): UpdatedItemResult + { + return new UpdatedItemResult( + $this->core->call( + 'crm.quote.update', + [ + 'id' => $id, + 'fields' => $fields + ] + ) + ); + } + + /** + * Count quotes by filter + * + * @param array{ + * ID?: int, + * ASSIGNED_BY_ID?: int, + * BEGINDATA?: string, + * CLIENT_ADDR?: string, + * CLOSED?: bool, + * CLOSEDATA?: string, + * COMMENTS?: string, + * COMPANY_ID?: int, + * CONTACT_ID?: int, + * CONTACT_IDS?: int[], + * CONTENT?: string, + * CREATED_BY_ID?: int, + * CURRENCY_ID?: string, + * DATE_CREATE?: string, + * DATE_MODIFY?: string, + * DEAL_ID?: int, + * LEAD_ID?: int, + * LOCATION_ID?: int, + * MODIFY_BY_ID?: int, + * MYCOMPANY_ID?: int, + * OPENED?: bool, + * OPPORTUNITY?: string, + * PERSON_TYPE_ID?: int, + * QUOTE_NUMBER?: string, + * STATUS_ID?: string, + * TAX_VALUE?: string, + * TERMS?: string, + * TITLE?: string, + * UTM_CAMPAIGN?: string, + * UTM_CONTENT?: string, + * UTM_MEDIUM?: string, + * UTM_SOURCE?: string, + * UTM_TERM?: string, + * } $filter + * + * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + * @throws \Bitrix24\SDK\Core\Exceptions\TransportException + */ + public function countByFilter(array $filter = []): int + { + return $this->list([], $filter, ['ID'], 1)->getCoreResponse()->getResponseData()->getPagination()->getTotal(); + } +} diff --git a/src/Services/CRM/Quote/Service/QuoteContact.php b/src/Services/CRM/Quote/Service/QuoteContact.php new file mode 100644 index 00000000..910504e4 --- /dev/null +++ b/src/Services/CRM/Quote/Service/QuoteContact.php @@ -0,0 +1,174 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Service; + +use Bitrix24\SDK\Attributes\ApiServiceMetadata; +use Bitrix24\SDK\Core\Contracts\CoreInterface; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\InvalidArgumentException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Core\Result\DeletedItemResult; +use Bitrix24\SDK\Core\Result\FieldsResult; +use Bitrix24\SDK\Core\Result\UpdatedItemResult; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\CRM\Common\ContactConnection; +use Bitrix24\SDK\Services\CRM\Quote\Result\QuoteContactConnectionResult; +use Psr\Log\LoggerInterface; +use Bitrix24\SDK\Attributes\ApiEndpointMetadata; + +#[ApiServiceMetadata(new Scope(['crm']))] +class QuoteContact extends AbstractService +{ + /** + * Get Field Descriptions for Estimate-Contact Connection + * + * @link https://apidocs.bitrix24.com/api-reference/crm/quote/index.html + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'crm.quote.contact.fields', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/index.html', + 'Get Field Descriptions for Estimate-Contact Connection' + )] + public function fields(): FieldsResult + { + return new FieldsResult($this->core->call('crm.quote.contact.fields')); + } + + /** + * Set a set of contacts associated with the specified estimate + * + * @param non-negative-int $quoteId + * @param ContactConnection[] $contactConnections + * @throws InvalidArgumentException + * @link https://apidocs.bitrix24.com/api-reference/crm/quote/index.html + */ + #[ApiEndpointMetadata( + 'crm.quote.contact.items.set', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/index.html', + 'Set a set of contacts associated with the specified estimate crm.quote.contact.items.set' + )] + public function setItems(int $quoteId, array $contactConnections): UpdatedItemResult + { + $items = []; + foreach ($contactConnections as $contactConnection) { + if (!$contactConnection instanceof ContactConnection) { + throw new InvalidArgumentException( + sprintf('array item «%s» must be «%s» type', gettype($contactConnection), ContactConnection::class) + ); + } + + $items[] = [ + 'CONTACT_ID' => $contactConnection->contactId, + 'SORT' => $contactConnection->sort, + 'IS_PRIMARY' => $contactConnection->isPrimary ? 'Y' : 'N' + ]; + } + + if ($items === []) { + throw new InvalidArgumentException('empty contact connections array'); + } + + return new UpdatedItemResult( + $this->core->call('crm.quote.contact.items.set', [ + 'id' => $quoteId, + 'items' => $items + ]) + ); + } + + /** + * Get a set of contacts associated with the specified estimate + * + * @param non-negative-int $quoteId + * @link https://apidocs.bitrix24.com/api-reference/crm/quote/index.html + */ + #[ApiEndpointMetadata( + 'crm.quote.contact.items.get', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/index.html', + 'Get a set of contacts associated with the specified estimate crm.quote.contact.items.get' + )] + public function get(int $quoteId): QuoteContactConnectionResult + { + return new QuoteContactConnectionResult($this->core->call('crm.quote.contact.items.get', [ + 'id' => $quoteId + ])); + } + + /** + * Get a set of contacts associated with the specified estimates + * + * @param non-negative-int $quoteId + * @link https://apidocs.bitrix24.com/api-reference/crm/quote/index.html + */ + #[ApiEndpointMetadata( + 'crm.quote.contact.items.delete', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/index.html', + 'Clear the set of contacts associated with the specified estimate' + )] + public function deleteItems(int $quoteId): DeletedItemResult + { + return new DeletedItemResult($this->core->call('crm.quote.contact.items.delete', [ + 'id' => $quoteId + ])); + } + + /** + * Add Contact to the Specified Estimate + * + * @param non-negative-int $quoteId + * @link https://apidocs.bitrix24.com/api-reference/crm/quote/index.html + */ + #[ApiEndpointMetadata( + 'crm.quote.contact.add', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/index.html', + 'Add Contact to the Specified Estimate' + )] + public function add(int $quoteId, ContactConnection $contactConnection): UpdatedItemResult + { + return new UpdatedItemResult($this->core->call('crm.quote.contact.add', [ + 'id' => $quoteId, + 'fields' => [ + 'CONTACT_ID' => $contactConnection->contactId, + 'SORT' => $contactConnection->sort, + 'IS_PRIMARY' => $contactConnection->isPrimary ? 'Y' : 'N' + ] + ])); + } + + /** + * Delete Contact from Specified Estimate + * + * @param non-negative-int $quoteId + * @param non-negative-int $contactId + * @link https://apidocs.bitrix24.com/api-reference/crm/quote/index.html + */ + #[ApiEndpointMetadata( + 'crm.quote.contact.delete', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/index.html', + 'Delete Contact from Specified Estimate' + )] + public function delete(int $quoteId, int $contactId): DeletedItemResult + { + return new DeletedItemResult($this->core->call('crm.quote.contact.delete', [ + 'id' => $quoteId, + 'fields' => [ + 'CONTACT_ID' => $contactId + ] + ])); + } +} diff --git a/src/Services/CRM/Quote/Service/QuoteProductRows.php b/src/Services/CRM/Quote/Service/QuoteProductRows.php new file mode 100644 index 00000000..3a224552 --- /dev/null +++ b/src/Services/CRM/Quote/Service/QuoteProductRows.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Service; + +use Bitrix24\SDK\Attributes\ApiEndpointMetadata; +use Bitrix24\SDK\Attributes\ApiServiceMetadata; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Core\Result\UpdatedItemResult; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\CRM\Quote\Result\QuoteProductRowItemsResult; +use Money\Currency; + +#[ApiServiceMetadata(new Scope(['crm']))] +class QuoteProductRows extends AbstractService +{ + /** + * Returns products inside the specified quote. + * + * @link https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-product-rows-get.html + * + * @param Currency|null $currency + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'crm.quote.productrows.get', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-product-rows-get.html', + 'Returns products inside the specified quote.' + )] + public function get(int $quoteId, Currency $currency = null): QuoteProductRowItemsResult + { + if (!$currency instanceof \Money\Currency) { + $res = $this->core->call('batch', [ + 'halt' => 0, + 'cmd' => [ + 'quote' => sprintf('crm.quote.get?ID=%s', $quoteId), + 'rows' => sprintf('crm.quote.productrows.get?ID=%s', $quoteId) + ], + ]); + $data = $res->getResponseData()->getResult(); + $currency = new Currency($data['result']['quote']['CURRENCY_ID']); + return new QuoteProductRowItemsResult($res, $currency); + } + + return new QuoteProductRowItemsResult( + $this->core->call( + 'crm.quote.productrows.get', + [ + 'id' => $quoteId, + ] + ), + $currency + ); + } + + + /** + * Creates or updates product entries inside the specified quote. + * + * @link https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-product-rows-set.html + * + * @param array $productRows + * + * @throws BaseException + * @throws TransportException + */ + #[ApiEndpointMetadata( + 'crm.quote.productrows.set', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/crm-quote-product-rows-set.html', + 'Creates or updates product entries inside the specified quote.' + )] + public function set(int $quoteId, array $productRows): UpdatedItemResult + { + return new UpdatedItemResult( + $this->core->call( + 'crm.quote.productrows.set', + [ + 'id' => $quoteId, + 'rows' => $productRows, + ] + ) + ); + } +} diff --git a/src/Services/CRM/Quote/Service/QuoteUserfield.php b/src/Services/CRM/Quote/Service/QuoteUserfield.php new file mode 100644 index 00000000..1de5a51c --- /dev/null +++ b/src/Services/CRM/Quote/Service/QuoteUserfield.php @@ -0,0 +1,236 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Services\CRM\Quote\Service; + +use Bitrix24\SDK\Attributes\ApiEndpointMetadata; +use Bitrix24\SDK\Attributes\ApiServiceMetadata; +use Bitrix24\SDK\Core\Contracts\CoreInterface; +use Bitrix24\SDK\Core\Credentials\Scope; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Core\Result\AddedItemResult; +use Bitrix24\SDK\Core\Result\DeletedItemResult; +use Bitrix24\SDK\Core\Result\UpdatedItemResult; +use Bitrix24\SDK\Services\AbstractService; +use Bitrix24\SDK\Services\CRM\Quote\Result\QuoteUserfieldResult; +use Bitrix24\SDK\Services\CRM\Quote\Result\QuoteUserfieldsResult; +use Bitrix24\SDK\Services\CRM\Userfield\Exceptions\UserfieldNameIsTooLongException; +use Bitrix24\SDK\Services\CRM\Userfield\Service\UserfieldConstraints; +use Psr\Log\LoggerInterface; + +#[ApiServiceMetadata(new Scope(['crm']))] +class QuoteUserfield extends AbstractService +{ + public function __construct(private readonly UserfieldConstraints $userfieldConstraints, CoreInterface $core, LoggerInterface $logger) + { + parent::__construct($core, $logger); + } + + /** + * Returns list of user quote fields by filter. + * + * @param array{ + * ID?: string, + * ENTITY_ID?: string, + * FIELD_NAME?: string, + * USER_TYPE_ID?: string, + * XML_ID?: string, + * SORT?: string, + * MULTIPLE?: string, + * MANDATORY?: string, + * SHOW_FILTER?: string, + * SHOW_IN_LIST?: string, + * EDIT_IN_LIST?: string, + * IS_SEARCHABLE?: string, + * EDIT_FORM_LABEL?: string, + * LIST_COLUMN_LABEL?: string, + * LIST_FILTER_LABEL?: string, + * ERROR_MESSAGE?: string, + * HELP_MESSAGE?: string, + * LIST?: string, + * SETTINGS?: string, + * } $order + * @param array{ + * ID?: string, + * ENTITY_ID?: string, + * FIELD_NAME?: string, + * USER_TYPE_ID?: string, + * XML_ID?: string, + * SORT?: string, + * MULTIPLE?: string, + * MANDATORY?: string, + * SHOW_FILTER?: string, + * SHOW_IN_LIST?: string, + * EDIT_IN_LIST?: string, + * IS_SEARCHABLE?: string, + * EDIT_FORM_LABEL?: string, + * LIST_COLUMN_LABEL?: string, + * LIST_FILTER_LABEL?: string, + * ERROR_MESSAGE?: string, + * HELP_MESSAGE?: string, + * LIST?: string, + * SETTINGS?: string, + * } $filter + * + * @throws BaseException + * @throws TransportException + * @link https://apidocs.bitrix24.com/api-reference/crm/quote/user-field/crm-quote-user-field-list.html + */ + #[ApiEndpointMetadata( + 'crm.quote.userfield.list', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/user-field/crm-quote-user-field-list.html', + 'Returns list of user quote fields by filter.' + )] + public function list(array $order, array $filter): QuoteUserfieldsResult + { + return new QuoteUserfieldsResult( + $this->core->call( + 'crm.quote.userfield.list', + [ + 'order' => $order, + 'filter' => $filter, + ] + ) + ); + } + + /** + * Created new user field for quotes. + * + * System limitation for field name - 20 characters. + * Prefix UF_CRM_is always added to the user field name. + * As a result, the actual name length - 13 characters. + * + * @param array{ + * FIELD_NAME?: string, + * USER_TYPE_ID?: string, + * XML_ID?: string, + * SORT?: string, + * MULTIPLE?: string, + * MANDATORY?: string, + * SHOW_FILTER?: string, + * SHOW_IN_LIST?: string, + * EDIT_IN_LIST?: string, + * IS_SEARCHABLE?: string, + * EDIT_FORM_LABEL?: string|array, + * LIST_COLUMN_LABEL?: string|array, + * LIST_FILTER_LABEL?: string|array, + * ERROR_MESSAGE?: string, + * HELP_MESSAGE?: string, + * LIST?: string, + * SETTINGS?: array, + * } $userfieldItemFields + * + * @throws BaseException + * @throws TransportException + * @throws UserfieldNameIsTooLongException + * @link https://apidocs.bitrix24.com/api-reference/crm/quote/user-field/crm-quote-user-field-add.html + * + */ + #[ApiEndpointMetadata( + 'crm.quote.userfield.add', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/user-field/crm-quote-user-field-add.html', + 'Created new user field for quotes.' + )] + public function add(array $userfieldItemFields): AddedItemResult + { + $this->userfieldConstraints->validName($userfieldItemFields['FIELD_NAME']); + + return new AddedItemResult( + $this->core->call( + 'crm.quote.userfield.add', + [ + 'fields' => $userfieldItemFields, + ] + ) + ); + } + + /** + * Deleted userfield for quotes + * + * + * @throws BaseException + * @throws TransportException + * @link https://apidocs.bitrix24.com/api-reference/crm/quote/user-field/crm-quote-user-field-delete.html + * + */ + #[ApiEndpointMetadata( + 'crm.quote.userfield.delete', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/user-field/crm-quote-user-field-delete.html', + 'Deleted userfield for quotes' + )] + public function delete(int $userfieldId): DeletedItemResult + { + return new DeletedItemResult( + $this->core->call( + 'crm.quote.userfield.delete', + [ + 'id' => $userfieldId, + ] + ) + ); + } + + /** + * Returns a userfield for quote by ID. + * + * + * @throws BaseException + * @throws TransportException + * @link https://apidocs.bitrix24.com/api-reference/crm/quote/user-field/crm-quote-user-field-get.html + */ + #[ApiEndpointMetadata( + 'crm.quote.userfield.get', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/user-field/crm-quote-user-field-get.html', + 'Returns a userfield for quote by ID.' + )] + public function get(int $userfieldItemId): QuoteUserfieldResult + { + return new QuoteUserfieldResult( + $this->core->call( + 'crm.quote.userfield.get', + [ + 'id' => $userfieldItemId, + ] + ) + ); + } + + /** + * Updates an existing user field for quotes. + * + * + * @throws BaseException + * @throws TransportException + * @link https://apidocs.bitrix24.com/api-reference/crm/quote/user-field/crm-quote-user-field-update.html + */ + #[ApiEndpointMetadata( + 'crm.quote.userfield.update', + 'https://apidocs.bitrix24.com/api-reference/crm/quote/user-field/crm-quote-user-field-update.html', + 'Updates an existing user field for quotes.' + )] + public function update(int $userfieldItemId, array $userfieldFieldsToUpdate): UpdatedItemResult + { + return new UpdatedItemResult( + $this->core->call( + 'crm.quote.userfield.update', + [ + 'id' => $userfieldItemId, + 'fields' => $userfieldFieldsToUpdate, + ] + ) + ); + } +} diff --git a/tests/CustomAssertions/CustomBitrix24Assertions.php b/tests/CustomAssertions/CustomBitrix24Assertions.php index 4635474f..2edecd92 100644 --- a/tests/CustomAssertions/CustomBitrix24Assertions.php +++ b/tests/CustomAssertions/CustomBitrix24Assertions.php @@ -141,7 +141,6 @@ protected function assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( break; case 'user': case 'crm_enum_ownertype': - case 'crm_lead': case 'integer': case 'int': $this->assertTrue( @@ -227,6 +226,9 @@ protected function assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( case 'object': case 'crm_company': case 'crm_contact': + case 'crm_deal': + case 'crm_lead': + case 'location': case 'product_file': if (str_contains($fieldCode, '_IDS') || str_contains($fieldCode, 'PHOTO') || diff --git a/tests/Integration/Services/CRM/Quote/Service/BatchTest.php b/tests/Integration/Services/CRM/Quote/Service/BatchTest.php new file mode 100644 index 00000000..c18ac093 --- /dev/null +++ b/tests/Integration/Services/CRM/Quote/Service/BatchTest.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\CRM\Quote\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\CRM\Quote\Service\Quote; +use Bitrix24\SDK\Tests\Integration\Fabric; +use PHPUnit\Framework\TestCase; + +/** + * Class BatchTest + * + * @package Bitrix24\SDK\Tests\Integration\Services\CRM\Quote\Service + */ +#[\PHPUnit\Framework\Attributes\CoversClass(\Bitrix24\SDK\Services\CRM\Quote\Service\Batch::class)] +class BatchTest extends TestCase +{ + protected Quote $quoteService; + + + protected function setUp(): void + { + $this->quoteService = Fabric::getServiceBuilder()->getCRMScope()->quote(); + } + + /** + * @throws BaseException + * @throws TransportException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch list quotes')] + public function testBatchList(): void + { + $itemId = $this->quoteService->add(['TITLE' => 'test quote'])->getId(); + $cnt = 0; + + foreach ($this->quoteService->batch->list([], ['ID' => $itemId], ['ID', 'NAME'], 1) as $item) { + $cnt++; + } + + self::assertGreaterThanOrEqual(1, $cnt); + + $this->quoteService->delete($itemId); + } + + /** + * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch add quote')] + public function testBatchAdd(): void + { + $items = []; + for ($i = 1; $i < 60; $i++) { + $items[] = ['TITLE' => 'TITLE-' . $i]; + } + + $cnt = 0; + $itemId = []; + foreach ($this->quoteService->batch->add($items) as $item) { + $cnt++; + $itemId[] = $item->getId(); + } + + self::assertEquals(count($items), $cnt); + + $cnt = 0; + foreach ($this->quoteService->batch->delete($itemId) as $cnt => $deleteResult) { + $cnt++; + } + + self::assertEquals(count($items), $cnt); + } + + /** + * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + */ + #[\PHPUnit\Framework\Attributes\TestDox('Batch delete quotes')] + public function testBatchDelete(): void + { + $quotes = []; + for ($i = 1; $i < 60; $i++) { + $quotes[] = ['TITLE' => 'TITLE-' . $i]; + } + + $cnt = 0; + $itemId = []; + foreach ($this->quoteService->batch->add($quotes) as $item) { + $cnt++; + $itemId[] = $item->getId(); + } + + self::assertEquals(count($quotes), $cnt); + + $cnt = 0; + foreach ($this->quoteService->batch->delete($itemId) as $cnt => $deleteResult) { + $cnt++; + } + + self::assertEquals(count($quotes), $cnt); + } + +} diff --git a/tests/Integration/Services/CRM/Quote/Service/QuoteContactTest.php b/tests/Integration/Services/CRM/Quote/Service/QuoteContactTest.php new file mode 100644 index 00000000..3e5a0718 --- /dev/null +++ b/tests/Integration/Services/CRM/Quote/Service/QuoteContactTest.php @@ -0,0 +1,173 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\CRM\Quote\Service; + + +use Bitrix24\SDK\Services\CRM\Common\ContactConnection; +use Bitrix24\SDK\Services\CRM\Quote\Service\QuoteContact; +use Bitrix24\SDK\Services\ServiceBuilder; +use Bitrix24\SDK\Tests\Builders\Services\CRM\ContactBuilder; +use Bitrix24\SDK\Tests\Integration\Fabric; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\TestCase; +use Bitrix24\SDK\Core; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; + +#[CoversClass(QuoteContact::class)] +#[CoversMethod(QuoteContact::class, 'fields')] +#[CoversMethod(QuoteContact::class, 'setItems')] +#[CoversMethod(QuoteContact::class, 'deleteItems')] +#[CoversMethod(QuoteContact::class, 'get')] +#[CoversMethod(QuoteContact::class, 'add')] +#[CoversMethod(QuoteContact::class, 'delete')] +class QuoteContactTest extends TestCase +{ + use CustomBitrix24Assertions; + + private ServiceBuilder $sb; + + private array $createdQuotes = []; + + private array $createdContacts = []; + + protected function setUp(): void + { + $this->sb = Fabric::getServiceBuilder(); + } + + protected function tearDown(): void + { + foreach ($this->sb->getCRMScope()->quote()->batch->delete($this->createdQuotes) as $result) { + // ### + } + + foreach ($this->sb->getCRMScope()->contact()->batch->delete($this->createdContacts) as $result) { + // ### + } + } + + public function testSet(): void + { + $quoteId = $this->sb->getCRMScope()->quote()->add(['TITLE' => 'test quote'])->getId(); + $this->createdQuotes[] = $quoteId; + + $contactIdOne = $this->sb->getCRMScope()->contact()->add((new ContactBuilder())->build())->getId(); + $this->createdContacts[] = $contactIdOne; + $contactIdTwo = $this->sb->getCRMScope()->contact()->add((new ContactBuilder())->build())->getId(); + $this->createdContacts[] = $contactIdTwo; + + $this->sb->getCRMScope()->quoteContact()->setItems($quoteId, [ + new ContactConnection($contactIdOne, 100, true), + new ContactConnection($contactIdTwo, 100, false), + ]); + + $connectedId = [$contactIdOne, $contactIdTwo]; + $connectedContacts = $this->sb->getCRMScope()->quoteContact()->get($quoteId)->getContactConnections(); + + foreach ($connectedContacts as $connectedContact) { + $this->assertContains($connectedContact->CONTACT_ID, $connectedId); + } + } + + public function testAdd(): void + { + $quoteId = $this->sb->getCRMScope()->quote()->add(['TITLE' => 'test quote'])->getId(); + $this->createdQuotes[] = $quoteId; + + $contactIdOne = $this->sb->getCRMScope()->contact()->add((new ContactBuilder())->build())->getId(); + $this->createdContacts[] = $contactIdOne; + + $this->assertTrue( + $this->sb->getCRMScope()->quoteContact()->add( + $quoteId, + new ContactConnection($contactIdOne, 100, true) + )->isSuccess() + ); + + $contactIdTwo = $this->sb->getCRMScope()->contact()->add((new ContactBuilder())->build())->getId(); + $this->createdContacts[] = $contactIdTwo; + + $this->assertTrue( + $this->sb->getCRMScope()->quoteContact()->add( + $quoteId, + new ContactConnection($contactIdTwo, 100, true) + )->isSuccess() + ); + + $connectedId = [$contactIdOne, $contactIdTwo]; + $connectedContacts = $this->sb->getCRMScope()->quoteContact()->get($quoteId)->getContactConnections(); + + foreach ($connectedContacts as $connectedContact) { + $this->assertContains($connectedContact->CONTACT_ID, $connectedId); + } + } + + public function testDeleteItems(): void + { + $quoteId = $this->sb->getCRMScope()->quote()->add(['TITLE' => 'test quote'])->getId(); + $this->createdQuotes[] = $quoteId; + + $contactIdOne = $this->sb->getCRMScope()->contact()->add((new ContactBuilder())->build())->getId(); + $this->createdContacts[] = $contactIdOne; + $contactIdTwo = $this->sb->getCRMScope()->contact()->add((new ContactBuilder())->build())->getId(); + $this->createdContacts[] = $contactIdTwo; + + $this->assertTrue( + $this->sb->getCRMScope()->quoteContact()->setItems($quoteId, [ + new ContactConnection($contactIdOne, 100, true), + new ContactConnection($contactIdTwo, 100, false), + ])->isSuccess() + ); + + $this->assertTrue($this->sb->getCRMScope()->quoteContact()->deleteItems($quoteId)->isSuccess()); + + $this->assertCount(0, $this->sb->getCRMScope()->quoteContact()->get($quoteId)->getContactConnections()); + } + + public function testDelete(): void + { + $quoteId = $this->sb->getCRMScope()->quote()->add(['TITLE' => 'test quote'])->getId(); + $this->createdQuotes[] = $quoteId; + + $contactIdOne = $this->sb->getCRMScope()->contact()->add((new ContactBuilder())->build())->getId(); + $this->createdContacts[] = $contactIdOne; + $contactIdTwo = $this->sb->getCRMScope()->contact()->add((new ContactBuilder())->build())->getId(); + $this->createdContacts[] = $contactIdTwo; + + $this->assertTrue( + $this->sb->getCRMScope()->quoteContact()->setItems($quoteId, [ + new ContactConnection($contactIdOne, 100, true), + new ContactConnection($contactIdTwo, 100, false), + ])->isSuccess() + ); + + $this->assertTrue($this->sb->getCRMScope()->quoteContact()->delete($quoteId, $contactIdTwo)->isSuccess()); + + $this->assertCount(1, $this->sb->getCRMScope()->quoteContact()->get($quoteId)->getContactConnections()); + } + + public function testSetWithEmptyConnections(): void + { + $this->expectException(Core\Exceptions\InvalidArgumentException::class); + $this->sb->getCRMScope()->quoteContact()->setItems(1, []); + } + + public function testSetWithWrongType(): void + { + $this->expectException(Core\Exceptions\InvalidArgumentException::class); + /** @phpstan-ignore-next-line */ + $this->sb->getCRMScope()->quoteContact()->setItems(1, [new \DateTime()]); + } +} \ No newline at end of file diff --git a/tests/Integration/Services/CRM/Quote/Service/QuoteProductRowsTest.php b/tests/Integration/Services/CRM/Quote/Service/QuoteProductRowsTest.php new file mode 100644 index 00000000..b31217fb --- /dev/null +++ b/tests/Integration/Services/CRM/Quote/Service/QuoteProductRowsTest.php @@ -0,0 +1,147 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\CRM\Quote\Service; + +use Money\Currencies\ISOCurrencies; +use Money\Currency; +use Money\Formatter\DecimalMoneyFormatter; +use Money\Money; +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\CRM\Common\Result\DiscountType; +use Bitrix24\SDK\Services\CRM\Quote\Result\QuoteProductRowItemResult; +use Bitrix24\SDK\Services\CRM\Quote\Service\Quote; +use Bitrix24\SDK\Services\CRM\Quote\Service\QuoteProductRows; +use Bitrix24\SDK\Tests\Builders\DemoDataGenerator; +use Bitrix24\SDK\Tests\Integration\Fabric; +use MoneyPHP\Percentage\Percentage; +use PHPUnit\Framework\TestCase; +use Typhoon\Reflection\TyphoonReflector; + +#[\PHPUnit\Framework\Attributes\CoversClass(\Bitrix24\SDK\Services\CRM\Quote\Service\QuoteProductRows::class)] +class QuoteProductRowsTest extends TestCase +{ + private Quote $quoteService; + + private QuoteProductRows $quoteProductRowsService; + + private DecimalMoneyFormatter $decimalMoneyFormatter; + + private TyphoonReflector $typhoonReflector; + + protected function setUp(): void + { + $this->quoteService = Fabric::getServiceBuilder()->getCRMScope()->quote(); + $this->quoteProductRowsService = Fabric::getServiceBuilder()->getCRMScope()->quoteProductRows(); + $this->decimalMoneyFormatter = new DecimalMoneyFormatter(new ISOCurrencies()); + $this->typhoonReflector = TyphoonReflector::build(); + } + + public function testAllSystemPropertiesAnnotated(): void + { + $quoteId = $this->quoteService->add(['TITLE' => 'test quote'])->getId(); + $this->quoteProductRowsService->set( + $quoteId, + [ + [ + 'PRODUCT_NAME' => sprintf('product name %s', time()), + 'PRICE' => $this->decimalMoneyFormatter->format(new Money(100000, DemoDataGenerator::getCurrency())), + ], + ] + ); + // get response from server with actual keys + $propListFromApi = array_keys($this->quoteProductRowsService->get($quoteId)->getCoreResponse()->getResponseData()->getResult()['result']['rows'][0]); + // parse keys from phpdoc annotation + $collection = $this->typhoonReflector->reflectClass(QuoteProductRowItemResult::class)->properties(); + $propsFromAnnotations = []; + foreach ($collection as $meta) { + if ($meta->isAnnotated() && !$meta->isNative()) { + $propsFromAnnotations[] = $meta->id->name; + } + } + + $this->assertEquals($propListFromApi, $propsFromAnnotations, + sprintf('in phpdocs annotations for class %s cant find fields from actual api response: %s', + QuoteProductRowItemResult::class, + implode(', ', array_values(array_diff($propListFromApi, $propsFromAnnotations))) + )); + + $this->quoteService->delete($quoteId); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testSet(): void + { + $quoteId = $this->quoteService->add(['TITLE' => sprintf('test quote %s', time())])->getId(); + $quote = $this->quoteService->get($quoteId)->quote(); + $price = new Money(100000, $quote->CURRENCY_ID); + $discount = new Money(50012, $quote->CURRENCY_ID); + $this::assertTrue( + $this->quoteProductRowsService->set( + $quoteId, + [ + [ + 'PRODUCT_NAME' => sprintf('product name %s', time()), + 'PRICE' => $this->decimalMoneyFormatter->format($price), + 'DISCOUNT_TYPE_ID' => 1, + 'DISCOUNT_SUM' => $this->decimalMoneyFormatter->format($discount) + ], + ] + )->isSuccess() + ); + $quoteProductRowItemsResult = $this->quoteProductRowsService->get($quoteId); + $this->assertCount(1, $quoteProductRowItemsResult->getProductRows()); + $productRow = $quoteProductRowItemsResult->getProductRows()[0]; + $this->assertEquals($price, $productRow->PRICE); + $this->assertEquals(DiscountType::monetary, $productRow->DISCOUNT_TYPE_ID); + $this->assertEquals($discount, $productRow->DISCOUNT_SUM); + $discount = $discount->multiply(100)->divide($this->decimalMoneyFormatter->format($price->add($discount))); + $calculatedPercentage = new Percentage((string)((int)$discount->getAmount() / 100)); + $this->assertEquals($calculatedPercentage, $productRow->DISCOUNT_RATE); + + $this->quoteService->delete($quoteId); + } + + public function testGet(): void + { + $quoteId = $this->quoteService->add(['TITLE' => sprintf('test quote %s', time())])->getId(); + $quote = $this->quoteService->get($quoteId)->quote(); + $price = new Money(100000, $quote->CURRENCY_ID); + $discount = new Money(0, $quote->CURRENCY_ID); + $this::assertTrue( + $this->quoteProductRowsService->set( + $quoteId, + [ + [ + 'PRODUCT_NAME' => sprintf('product name %s', time()), + 'PRICE' => $this->decimalMoneyFormatter->format($price), + ], + ] + )->isSuccess() + ); + $quoteProductRowItemsResult = $this->quoteProductRowsService->get($quoteId); + $this->assertCount(1, $quoteProductRowItemsResult->getProductRows()); + $productRow = $quoteProductRowItemsResult->getProductRows()[0]; + $this->assertEquals($price, $productRow->PRICE); + $this->assertEquals(DiscountType::percentage, $productRow->DISCOUNT_TYPE_ID); + $this->assertEquals($discount, $productRow->DISCOUNT_SUM); + $this->assertEquals(Percentage::zero(), $productRow->DISCOUNT_RATE); + + $this->quoteService->delete($quoteId); + } + +} diff --git a/tests/Integration/Services/CRM/Quote/Service/QuoteTest.php b/tests/Integration/Services/CRM/Quote/Service/QuoteTest.php new file mode 100644 index 00000000..ac6cf2ad --- /dev/null +++ b/tests/Integration/Services/CRM/Quote/Service/QuoteTest.php @@ -0,0 +1,153 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\CRM\Quote\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Core; +use Bitrix24\SDK\Services\CRM\Quote\Result\QuoteItemResult; +use Bitrix24\SDK\Services\CRM\Quote\Service\Quote; +use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions; +use Bitrix24\SDK\Tests\Integration\Fabric; +use PHPUnit\Framework\Attributes\CoversFunction; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\TestCase; + +/** + * Class QuoteTest + * + * @package Bitrix24\SDK\Tests\Integration\Services\CRM\Quote\Service + */ +#[CoversMethod(Quote::class,'add')] +#[CoversMethod(Quote::class,'delete')] +#[CoversMethod(Quote::class,'get')] +#[CoversMethod(Quote::class,'list')] +#[CoversMethod(Quote::class,'fields')] +#[CoversMethod(Quote::class,'update')] +#[\PHPUnit\Framework\Attributes\CoversClass(\Bitrix24\SDK\Services\CRM\Quote\Service\Quote::class)] +class QuoteTest extends TestCase +{ + use CustomBitrix24Assertions; + protected Quote $quoteService; + + protected function setUp(): void + { + $this->quoteService = Fabric::getServiceBuilder()->getCRMScope()->quote(); + } + + public function testAllSystemFieldsAnnotated(): void + { + $propListFromApi = (new Core\Fields\FieldsFilter())->filterSystemFields(array_keys($this->quoteService->fields()->getFieldsDescription())); + $this->assertBitrix24AllResultItemFieldsAnnotated($propListFromApi, QuoteItemResult::class); + } + + public function testAllSystemFieldsHasValidTypeAnnotation():void + { + $allFields = $this->quoteService->fields()->getFieldsDescription(); + $systemFieldsCodes = (new Core\Fields\FieldsFilter())->filterSystemFields(array_keys($allFields)); + $systemFields = array_filter($allFields, static fn($code): bool => in_array($code, $systemFieldsCodes, true), ARRAY_FILTER_USE_KEY); + + $this->assertBitrix24AllResultItemFieldsHasValidTypeAnnotation( + $systemFields, + QuoteItemResult::class); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testAdd(): void + { + self::assertGreaterThan(1, $this->quoteService->add(['TITLE' => 'test quote'])->getId()); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testDelete(): void + { + self::assertTrue($this->quoteService->delete($this->quoteService->add(['TITLE' => 'test quote 1'])->getId())->isSuccess()); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testFields(): void + { + self::assertIsArray($this->quoteService->fields()->getFieldsDescription()); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testGet(): void + { + self::assertGreaterThan( + 1, + $this->quoteService->get($this->quoteService->add(['TITLE' => 'test Quote 2'])->getId())->quote()->ID + ); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testList(): void + { + $this->quoteService->add(['TITLE' => 'test']); + self::assertGreaterThanOrEqual(1, $this->quoteService->list([], [], ['ID', 'TITLE'])->getQuotes()); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testUpdate(): void + { + $addedItemResult = $this->quoteService->add(['TITLE' => 'test quote']); + $newTitle = 'test2'; + + self::assertTrue($this->quoteService->update($addedItemResult->getId(), ['TITLE' => $newTitle])->isSuccess()); + self::assertEquals($newTitle, $this->quoteService->get($addedItemResult->getId())->quote()->TITLE); + } + + /** + * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + * @throws \Bitrix24\SDK\Core\Exceptions\TransportException + */ + public function testCountByFilter(): void + { + $before = $this->quoteService->countByFilter(); + + $newItemsCount = 20; + $items = []; + for ($i = 1; $i <= $newItemsCount; $i++) { + $items[] = ['TITLE' => 'TITLE-' . $i]; + } + + $cnt = 0; + foreach ($this->quoteService->batch->add($items) as $item) { + $cnt++; + } + + self::assertEquals(count($items), $cnt); + + $after = $this->quoteService->countByFilter(); + + $this->assertEquals($before + $newItemsCount, $after); + } +} diff --git a/tests/Integration/Services/CRM/Quote/Service/QuoteUserfieldTest.php b/tests/Integration/Services/CRM/Quote/Service/QuoteUserfieldTest.php new file mode 100644 index 00000000..78a16023 --- /dev/null +++ b/tests/Integration/Services/CRM/Quote/Service/QuoteUserfieldTest.php @@ -0,0 +1,108 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\CRM\Quote\Service; + +use Bitrix24\SDK\Services\CRM\Quote\Service\QuoteUserfield; +use Bitrix24\SDK\Tests\Builders\Services\CRM\Userfield\SystemUserfieldBuilder; +use Bitrix24\SDK\Tests\Integration\Fabric; +use Generator; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\TestCase; + +#[CoversClass(QuoteUserfield::class)] +#[CoversMethod(QuoteUserfield::class, 'add')] +#[CoversMethod(QuoteUserfield::class, 'get')] +#[CoversMethod(QuoteUserfield::class, 'list')] +#[CoversMethod(QuoteUserfield::class, 'delete')] +#[CoversMethod(QuoteUserfield::class, 'update')] +class QuoteUserfieldTest extends TestCase +{ + protected QuoteUserfield $userfieldService; + + protected function setUp(): void + { + $this->userfieldService = Fabric::getServiceBuilder()->getCRMScope()->quoteUserfield(); + } + + /** + * @throws \Exception + */ + public static function systemUserfieldsDemoDataDataProvider(): Generator + { + yield 'user type id string' => [ + (new SystemUserfieldBuilder())->build(), + ]; + + mt_srand(); + yield 'user type id integer' => [ + (new SystemUserfieldBuilder('integer'))->build(), + ]; + } + + #[DataProvider('systemUserfieldsDemoDataDataProvider')] + public function testAdd(array $newUserFieldItem): void + { + self::assertGreaterThanOrEqual(1, $this->userfieldService->add($newUserFieldItem)->getId()); + } + + #[DataProvider('systemUserfieldsDemoDataDataProvider')] + public function testDelete(array $newUserFieldItem): void + { + $newUserfieldId = $this->userfieldService->add($newUserFieldItem)->getId(); + $this->assertTrue($this->userfieldService->delete($newUserfieldId)->isSuccess()); + } + + #[DataProvider('systemUserfieldsDemoDataDataProvider')] + public function testGet(array $newUserFieldItem): void + { + $newUserfieldId = $this->userfieldService->add($newUserFieldItem)->getId(); + $quoteUserfieldItemResult = $this->userfieldService->get($newUserfieldId)->userfieldItem(); + $this->assertEquals($newUserfieldId, $quoteUserfieldItemResult->ID); + $this->assertEquals($newUserFieldItem['USER_TYPE_ID'], $quoteUserfieldItemResult->USER_TYPE_ID); + $this->assertEquals('UF_CRM_' . $newUserFieldItem['FIELD_NAME'], $quoteUserfieldItemResult->FIELD_NAME); + $this->assertEquals($newUserFieldItem['XML_ID'], $quoteUserfieldItemResult->XML_ID); + } + + #[DataProvider('systemUserfieldsDemoDataDataProvider')] + public function testUpdate(array $newUserFieldItem): void + { + $newUserfieldId = $this->userfieldService->add($newUserFieldItem)->getId(); + $quoteUserfieldItemResult = $this->userfieldService->get($newUserfieldId)->userfieldItem(); + $this->assertEquals($newUserfieldId, $quoteUserfieldItemResult->ID); + $this->assertEquals($newUserFieldItem['USER_TYPE_ID'], $quoteUserfieldItemResult->USER_TYPE_ID); + $this->assertEquals('UF_CRM_' . $newUserFieldItem['FIELD_NAME'], $quoteUserfieldItemResult->FIELD_NAME); + $this->assertEquals($newUserFieldItem['XML_ID'], $quoteUserfieldItemResult->XML_ID); + + $this->assertTrue( + $this->userfieldService->update( + $newUserfieldId, + [ + 'EDIT_FORM_LABEL' => $newUserFieldItem['EDIT_FORM_LABEL']['en'] . 'QQQ', + ] + )->isSuccess() + ); + + $ufFieldAfter = $this->userfieldService->get($newUserfieldId)->userfieldItem(); + $this->assertEquals($quoteUserfieldItemResult->EDIT_FORM_LABEL['en'] . 'QQQ', $ufFieldAfter->EDIT_FORM_LABEL['en']); + } + + public function testList(): void + { + $quoteUserfieldsResult = $this->userfieldService->list([], []); + $this->assertGreaterThanOrEqual(0, count($quoteUserfieldsResult->getUserfields())); + } + +} diff --git a/tests/Integration/Services/CRM/Quote/Service/QuoteUserfieldUseCaseTest.php b/tests/Integration/Services/CRM/Quote/Service/QuoteUserfieldUseCaseTest.php new file mode 100644 index 00000000..8d0badab --- /dev/null +++ b/tests/Integration/Services/CRM/Quote/Service/QuoteUserfieldUseCaseTest.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the MIT-LICENSE.txt + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Bitrix24\SDK\Tests\Integration\Services\CRM\Quote\Service; + +use Bitrix24\SDK\Core\Exceptions\BaseException; +use Bitrix24\SDK\Core\Exceptions\TransportException; +use Bitrix24\SDK\Services\CRM\Quote\Service\Quote; +use Bitrix24\SDK\Services\CRM\Quote\Service\QuoteUserfield; +use Bitrix24\SDK\Tests\Integration\Fabric; +use PHPUnit\Framework\TestCase; + +#[\PHPUnit\Framework\Attributes\CoversClass(\Bitrix24\SDK\Services\CRM\Quote\Service\QuoteUserfield::class)] +class QuoteUserfieldUseCaseTest extends TestCase +{ + protected Quote $quoteService; + + protected QuoteUserfield $quoteUserfieldService; + + protected int $quoteUserfieldId; + + /** + * @throws \Bitrix24\SDK\Services\CRM\Userfield\Exceptions\UserfieldNameIsTooLongException + * @throws \Bitrix24\SDK\Core\Exceptions\TransportException + * @throws \Bitrix24\SDK\Core\Exceptions\InvalidArgumentException + * @throws \Bitrix24\SDK\Core\Exceptions\BaseException + */ + protected function setUp(): void + { + $this->quoteService = Fabric::getServiceBuilder()->getCRMScope()->quote(); + $this->quoteUserfieldService = Fabric::getServiceBuilder()->getCRMScope()->quoteUserfield(); + + $this->quoteUserfieldId = $this->quoteUserfieldService->add( + [ + 'FIELD_NAME' => sprintf('%s%s', substr((string)random_int(0, PHP_INT_MAX), 0, 3), time()), + 'EDIT_FORM_LABEL' => [ + 'ru' => 'тест uf тип string', + 'en' => 'test uf type string', + ], + 'LIST_COLUMN_LABEL' => [ + 'ru' => 'тест uf тип string', + 'en' => 'test uf type string', + ], + 'USER_TYPE_ID' => 'string', + 'XML_ID' => 'b24phpsdk_type_string', + 'SETTINGS' => [ + 'DEFAULT_VALUE' => 'hello world', + ], + ] + )->getId(); + } + + protected function tearDown(): void + { + $this->quoteUserfieldService->delete($this->quoteUserfieldId); + } + + /** + * @throws BaseException + * @throws TransportException + */ + public function testOperationsWithUserfieldFromQuoteItem(): void + { + // get userfield metadata + $quoteUserfieldItemResult = $this->quoteUserfieldService->get($this->quoteUserfieldId)->userfieldItem(); + $ufOriginalFieldName = $quoteUserfieldItemResult->getOriginalFieldName(); + $ufFieldName = $quoteUserfieldItemResult->FIELD_NAME; + + // add quote with uf value + $fieldNameValue = 'test field value'; + $newQuoteId = $this->quoteService->add( + [ + 'TITLE' => 'test quote', + $ufFieldName => $fieldNameValue, + ] + )->getId(); + $quote = $this->quoteService->get($newQuoteId)->quote(); + $this->assertEquals($fieldNameValue, $quote->getUserfieldByFieldName($ufOriginalFieldName)); + + // update quote userfield value + $newUfValue = 'test 2'; + $this->assertTrue( + $this->quoteService->update( + $quote->ID, + [ + $ufFieldName => $newUfValue, + ] + )->isSuccess() + ); + $quoteItemResult = $this->quoteService->get($quote->ID)->quote(); + $this->assertEquals($newUfValue, $quoteItemResult->getUserfieldByFieldName($ufOriginalFieldName)); + } + +}