Giter Club home page Giter Club logo

amocrm-api-php's Introduction

amoCRM API Library

amoCRM API Library

Latest Version Build Status Total Downloads

В данном пакете представлен API клиент с поддержкой основных сущностей и авторизацией по протоколу OAuth 2.0 в amoCRM. Для работы библиотеки требуется PHP версии не ниже 7.1.

Оглавление

Установка

Установить библиотеку можно с помощью composer:

composer require amocrm/amocrm-api-library

Начало работы и авторизация

Для начала использования вам необходимо создать объект библиотеки:

$apiClient = new \AmoCRM\Client\AmoCRMApiClient($clientId, $clientSecret, $redirectUri);

Также предоставляется фабрика для создания объектов \AmoCRM\Client\AmoCRMApiClientFactory. Для ее использования вам нужно реализовать интерфейс \AmoCRM\OAuth\OAuthConfigInterface и \AmoCRM\OAuth\OAuthServiceInterface

$apiClientFactory = new \AmoCRM\Client\AmoCRMApiClientFactory($oAuthConfig, $oAuthService);
$apiClient = $apiClientFactory->make();

При использовании фабрики вам не нужно устанавливать callback onAccessTokenRefresh, при обновлении токена будет вызван метод saveOAuthToken из $oAuthService (\AmoCRM\OAuth\OAuthServiceInterface).

Затем необходимо создать объект (\League\OAuth2\Client\Token\AccessToken) Access токена из вашего хранилища токенов и установить его в API клиент.

Также необходимо установить домен аккаунта amoCRM в виде СУБДОМЕН.amocrm.(ru/com).

Вы можете установить функцию-callback на событие обновления Access токена, если хотите дополнительно обрабатывать новый токен (например сохранять его в хранилище токенов):

$apiClient->setAccessToken($accessToken)
        ->setAccountBaseDomain($accessToken->getValues()['baseDomain'])
        ->onAccessTokenRefresh(
            function (\League\OAuth2\Client\Token\AccessTokenInterface $accessToken, string $baseDomain) {
                saveToken(
                    [
                        'accessToken' => $accessToken->getToken(),
                        'refreshToken' => $accessToken->getRefreshToken(),
                        'expires' => $accessToken->getExpires(),
                        'baseDomain' => $baseDomain,
                    ]
                );
            });

Отправить пользователя на страницу авторизации можно 2мя способами:

  1. Отрисовав кнопку на сайт:
$apiClient->getOAuthClient()->getOAuthButton(
            [
                'title' => 'Установить интеграцию',
                'compact' => true,
                'class_name' => 'className',
                'color' => 'default',
                'error_callback' => 'handleOauthError',
                'state' => $state,
            ]
        );

Для аккаунтов kommo.com - добавьте параметр is_kommo => true,.

  1. Отправив пользователя на страницу авторизации
$authorizationUrl = $apiClient->getOAuthClient()->getAuthorizeUrl([
            'state' => $state,
            'mode' => 'post_message', //post_message - редирект произойдет в открытом окне, popup - редирект произойдет в окне родителе
        ]);

header('Location: ' . $authorizationUrl);

Для получения Access Token можно использовать следующий код в обработчике, который будет находиться по адресу, указанному в redirect_uri

$accessToken = $apiClient->getOAuthClient()->getAccessTokenByCode($_GET['code']);

Пример авторизации можно посмотреть в файле examples/get_token.php

Авторизация с правами конкретного пользователя аккаунта

Начиная с версии 1.4.0 появилась возможность авторизоваться с правами конкретного пользователя, если токен был выпущен администратором аккаунта.

Для авторизации под пользователем аккаунта - необходимо задать ID пользователя у объекта типа \AmoCRM\Client\AmoCRMApiClient. Метод вернет новый объект с установленным контекстом.

$apiClient = new \AmoCRM\Client\AmoCRMApiClient($clientId, $clientSecret, $redirectUri);
$apiClientWithContext = $apiClient->withContextUserId(123);

Установка кастомного User Agent

Начиная с версии 1.5.0 появилась возможность указать свой User Agent для запросов с библиотекой.

$apiClient = new \AmoCRM\Client\AmoCRMApiClient($clientId, $clientSecret, $redirectUri);
$apiClient = $apiClient->setUserAgnet('App Name');

Установка кастомного callback-обработчика ответа от сервера

Начиная с версии 1.9.0 появилась возможность устанавливать callback-обработчик ответа от сервера.

Вы можете установить функцию-callback на событие обработки ответа, если есть необходимость в дополнительной логике (например логировать ответ от сервера или же переопределить обработку 204 кода ответа).

Если нет необходимости в отработке стандартной логики обработки ответа, то callback должен возвращать true

$apiClient = new \AmoCRM\Client\AmoCRMApiClient($clientId, $clientSecret, $redirectUri);

$this->apiClient
     ->setCheckHttpStatusCallback(
         function (ResponseInterface $response, $decodedBody) {
             if ($response->getStatusCode() === 204) {
                 return true;
             }

             $this->logger->info('Response: ', $decodedBody);
         }
     );

Авторизация с долгоживущим токеном

Не так давно в amoCRM появилась возможность создавать долгоживущие токены. Их можно легко использовать с этой библиотекой.

Для начала использования вам необходимо создать объект библиотеки:

$apiClient = new \AmoCRM\Client\AmoCRMApiClient();

После этого нужно создать объект AmoCRM\Client\LongLivedAccessToken, который будет использоваться с запросами в API.

$longLivedAccessToken = new LongLivedAccessToken($accessToken);

Затем нужно установить токен и адресс аккаунта в объект библиотеки:

$apiClient->setAccessToken($longLivedAccessToken)
    ->setAccountBaseDomain('example.amocrm.ru');

После этих простых шагов, вы сможете делать запросы в amoCRM до тех пор, пока токен не истечет или его не отзовут. В случае отзыва или истечения токена - при выполнении запроса - упадет ошибка с http кодом 401.

Подход к работе с библиотекой

В библиотеке используется сервисный подход. Для каждой сущности имеется сервис. Для каждого метода имеется свой объект коллекции и модели. Работа с данными происходит через коллекции и методы библиотеки.

Модели и коллекции имеют методы toArray() и toApi(), методы возвращают представление объекта в виде массива и в виде данных, отправляемых в API.

Также для работы с коллекциями имеются следующие методы:

  1. add(BaseApiModel $model): self - добавляет модель в конец коллекции.
  2. prepend(BaseApiModel $value): self - добавляет модель в начало коллекции.
  3. all(): array - возвращает массив моделей в коллекции.
  4. first(): ?BaseApiModel - получение первой модели в коллекции.
  5. last(): ?BaseApiModel - получение последней модели в коллекции.
  6. count(): int - получение кол-ва элементов в коллекции.
  7. isEmpty(): bool - проверяет, что коллекция не пустая.
  8. getBy($key, $value): ?BaseApiModel - получение модели по значению ключа.
  9. replaceBy($key, $value, BaseApiModel $replacement): void - замена модели по значению ключа.
  10. removeBy($key, $value): int - удаление моделей по значению ключа, возвращает количество удаленных моделей.
  11. removeFirstBy($key, $value): bool - удаление первой модели по значению ключа, возвращает true если модель была удалена.
  12. chunk(int $size): array - разделение коллекции на массив состоящий из коллекций определенной длины.
  13. pluck(string $column): array - получение массива значений моделей коллекции по названию свойства.

При работе с библиотекой необходимо не забывать о лимитах API amoCRM. Для оптимальной работы с данными лучше всего создавать/изменять за раз не более 50 сущностей в методах, где есть пакетная обработка.

Нужно не забывать про обработку ошибок, а также не забывать о безопасности хранилища токенов. Утечка токена грозит потерей доступа к аккаунту.

Поддерживаемые методы и сервисы

Библиотека поддерживает большое количество методов API. Методы сгруппированы в объекты-сервисы. Получить объект сервиса можно вызвав необходимый метод у библиотеки, например:

$leadsService = $apiClient->leads();

В данный момент доступны следующие сервисы:

Сервис Цель сервиса
notes Примечание сущности
tags Теги сущностей
tasks Задачи
leads Сделки
contacts Контакты
companies Компании
catalogs Каталоги
catalogElements Элементы каталогов
customFields Пользовательские поля
customFieldGroups Группы пользовательских полей
account Информация об аккаунте
roles Роли пользователей
users Роли юзеров
customersSegments Сегменты покупателей
events Список событий
webhooks Вебхуки
unsorted Неразобранное
pipelines Воронки сделок
statuses Статусы сделок
widgets Виджеты
lossReason Причины отказа
transactions Покупки покупателей
customers Покупатели
customersStatuses Сегменты покупателя
customersBonusPoints Бонусные баллы покупателя
calls Звонки
products Товары
links Массовая привязка сущностей
shortLinks Короткие ссылки
talks Беседы
sources Источники
chatTemplates Шаблоны чатов
entitySubscriptions Подписчики сущности
getOAuthClient oAuth сервис
getRequest Голые запросы
files Файлы
entityFiles Связь файлов с сущностями
websiteButtons Кнопка на сайт

Для большинства сервисов есть базовый набор методов:

  1. getOne - Получить 1 сущность

    1. id (int|string) - id сущности
    2. with (array) - массив параметров with, которые поддерживает модель сервиса
    3. Результатом выполнения будет модель сущности
    getOne($id, array $with => []);
  2. get Получить несколько сущностей:

    1. filter (BaseEntityFilter) - фильтр для сущности
    2. with (array) - массив параметров with, которые поддерживает модель сервиса
    3. Результатом выполнения будет коллекция сущностей
    get(BaseEntityFilter $filter = null, array $with = []);
  3. addOne Создать одну сущность:

    1. model (BaseApiModel) - модель создаваемой сущности
    2. Результатом выполнения будет модель сущности
    addOne(BaseApiModel $model);
  4. add Создать сущности пакетно:

    1. collection (BaseApiCollection) - коллекция моделей создаваемой сущности
    2. Результатом выполнения будет коллекция моделей сущности
    add(BaseApiCollection $collection);
  5. updateOne Обновить одну сущность:

    1. model (BaseApiModel) - модель создаваемой сущности
    2. Результатом выполнения будет модель сущности
    updateOne(BaseApiModel $model);
  6. update Обновить сущности пакетно:

    1. collection (BaseApiCollection) - коллекция моделей создаваемой сущности
    2. Результатом выполнения будет коллекция моделей сущности
    update(BaseApiCollection $collection);
  7. syncOne Синхронизировать одну модель с сервером:

    1. model (BaseApiModel) - коллекция моделей создаваемой сущности
    2. with (array) - массив параметров with, которые поддерживает модель сервиса
    3. Результатом выполнения будет коллекция моделей сущности
    syncOne(BaseApiModel $model, $with = []);

Не все методы доступны во всех сервисах. В случае их вызова будет выброшены Exception.

Некоторые сервисы имеют специфичные методы, ниже рассмотрим сервисы, которые имеют специфичные методы.

Методы доступные в сервисе leads:

  1. addComplex Создать сделки пакетно со связанным контакт и компанией через комплексный метод с поддержкой контроля дублей
    1. collection (LeadsCollection) - коллекция моделей создаваемой сущности
    2. Результатом выполнения будет новая коллекция созданных сущностей
    addComplex(LeadsCollection $collection);
  2. addOneComplex Создать одну сделку со связанным контакт и компанией через комплексный метод с поддержкой контроля дублей
    1. collection (LeadsCollection) - коллекция моделей создаваемой сущности
    2. Результатом выполнения будет новая модель созданной сделки
    addOneComplex(LeadModel $model);

Подробнее про использование метода комплексного создания смотрите в примере

Методы доступные в сервисе getOAuthClient:

  1. getAuthorizeUrl получение ссылки на авторизация

    1. options (array)
      1. state (string) состояние приложения
    2. Результатом выполнения будет строка со ссылкой на авторизация приложения
    getAuthorizeUrl(array $options = []);
  2. getAccessTokenByCode получение access токена по коду авторизации

    1. code (string) код авторизации
    2. Результатом выполнения будет объект (AccessTokenInterface)
    getAccessTokenByCode(string $code);
  3. getAccessTokenByRefreshToken получение access токена по refresh токену

    1. accessToken (AccessTokenInterface) объект access токена
    2. Результатом выполнения будет объект (AccessTokenInterface)
    getAccessTokenByRefreshToken(AccessTokenInterface $accessToken);
  4. setBaseDomain установка базового домена, куда будут отправляться запросы необходимые для работы с токенами

    1. domain (string)
    setBaseDomain(string $domain);
  5. setAccessTokenRefreshCallback установка callback, который будет вызван при обновлении access токена

    1. function (callable)
    setAccessTokenRefreshCallback(callable $function);
  6. getOAuthButton установка callback, который будет вызван при обновлении access токена

    1. options (array)
      1. state (string) состояние приложения
      2. color (string)
      3. title (string)
      4. compact (bool)
      5. class_name (string)
      6. error_callback (string)
      7. mode (string)
    2. Результатом выполнения будет строка с HTML кодом кнопки авторизации
    getOAuthButton(array $options = []);
  7. exchangeApiKey метод для обмена API ключа на код авторизации

    1. login - email пользователя, для которого обменивается API ключ
    2. apiKey - API ключ пользователя
    3. Код авторизации будет прислан на указанный в настройках приложения redirect_uri
    exchangeApiKey(string $login, string $apiKey);

Методы связей доступны в сервисах leads, contacts, companies, customers:

  1. link Привязать сущность

    1. model (BaseApiModel) - модель главной сущности
    2. links (LinksCollection|LinkModel) - коллекция или модель связи
    3. Результатом выполнения является коллекция связей (LinksCollection)
    link(BaseApiModel $model, $linkedEntities);
  2. getLinks Получить связи сущности

    1. model (BaseApiModel) - модель главной сущности
    2. filter (LinksFilter) - фильтр для связей
    3. Результатом выполнения является коллекция связей (LinksCollection)
    getLinks(BaseApiModel $model, LinksFilter $filter);
  3. unlink Отвязать сущность

    1. model (BaseApiModel) - модель главной сущности
    2. links (LinksCollection|LinkModel) - коллекция или модель связи
    3. Результатом выполнения является bool значение
    unlink(BaseApiModel $model, $linkedEntities);

Методы удаления доступны в сервисах transactions, lossReasons, statuses, pipelines, customFields, customFieldsGroups, roles, customersStatuses, entityFiles, files:

  1. delete

    1. model (BaseApiModel) - модель сущности
    2. Результатом выполнения является bool значение
    deleteOne(BaseApiModel $model);
  2. deleteOne

    1. collection (BaseApiCollection) - коллекция моделей сущностей
    2. Результатом выполнения является bool значение
    deleteOne(BaseApiModel $model);

Методы доступные в сервисе customers:

  1. setMode Смена режима покупателей (периодические покупки или сегментация). Если покупатели выключены - то они будут включены.
    1. mode (string) - тип режима (periodicity или segments)
    2. isEnabled (bool) - включен ли функционал покупателей, по-умолчанию - true
    3. Результатом выполнения является строка названия включенного режима или null в случае отключения функционала
    setMode(string $mode, bool $isEnabled = true);

Методы доступные в сервисе customersBonusPoints:

  1. earnPoints Начисляет бонусные баллы покупателю

    1. model (BonusPointsActionModel) - модель в которой Id покупателя и количество баллов для начисления
    2. Результатом выполнения является обновленное количество бонусных баллов покупателя или null в случае если произошла ошибка
    earnPoints(BonusPointsActionModel $bonusPointsActionModel)
  2. redeemPoints Списывает бонусные баллы покупателя

    1. model (BonusPointsActionModel) - модель в которой Id покупателя и количество баллов для списания
    2. Результатом выполнения является обновленное количество бонусных баллов покупателя или null в случае если произошла ошибка
    redeemPoints(BonusPointsActionModel $bonusPointsActionModel)

Методы доступные в сервисе notes, entitySubscriptions:

  1. getByParentId Получение данных по ID родительской сущности
    1. parentId - ID родительской сущности
    2. filter (BaseEntityFilter) - фильтр
    3. with (array) - массив параметров with, которые поддерживает модель сервиса
    getByParentId(int $parentId, BaseEntityFilter $filter = null, array $with = []);

Методы доступные в сервисе account

  1. getCurrent
    1. with (array) - массив параметров with, которые поддерживает модель сервиса
    2. Результатом выполнения является модель AccountModel
    getCurrent(array $with = []);

Методы доступные в сервисе unsorted

  1. addOne Создать одну сущность:

    1. model (BaseApiModel) - модель создаваемой сущности
    2. Результатом выполнения будет модель сущности
    addOne(BaseApiModel $model);
  2. add Создать сущности пакетно:

    1. collection (BaseApiCollection) - коллекция моделей создаваемой сущности
    2. Результатом выполнения будет коллекция моделей сущности
    add(BaseApiCollection $collection);
  3. link

    1. model (BaseApiModel) - модель неразобранного
    2. body (array) - массив дополнительной информации для привязки
    3. Результатом выполнения будет модель LinkUnsortedModel
    link(BaseApiModel $unsortedModel, $body = []);
  4. accept

    1. model (BaseApiModel) - модель неразобранного
    2. body (array) - массив дополнительной информации для принятия
    3. Результатом выполнения будет модель AcceptUnsortedModel
    accept(BaseApiModel $unsortedModel, $body = []);
  5. decline

    1. model (BaseApiModel) - модель неразобранного
    2. body (array) - массив дополнительной информации для отклонения
    3. Результатом выполнения будет модель DeclineUnsortedModel
    decline(BaseApiModel $unsortedModel, $body = []);
  6. summary

    1. filter (BaseEntityFilter) - фильтр для сущности
    2. Результатом выполнения будет модель UnsortedSummaryModel
    summary(BaseEntityFilter $filter);

Методы доступные в сервисе webhooks

  1. subscribe

    1. model (WebhookModel) - модель вебхука
    2. Результатом выполнения является модель WebhookModel
    subscribe(WebhookModel $webhookModel);
  2. unsubscribe

    1. model (WebhookModel) - модель вебхука
    2. Результатом выполнения является bool значение
    unsubscribe(WebhookModel $webhookModel);

Методы доступные в сервисе widgets

  1. install

    1. model (WidgetModel) - модель виджета
    2. Результатом выполнения является модель WidgetModel
    install(WidgetModel $widgetModel);
  2. uninstall

    1. model (WidgetModel) - модель виджета
    2. Результатом выполнения является модель WidgetModel
    uninstall(WidgetModel $widgetModel);

Методы доступные в сервисе products

  1. settings

    1. Результатом выполнения является модель ProductsSettingsModel
    settings();
  2. updateSettings

    1. model (ProductsSettingsModel) - модель виджета
    2. Результатом выполнения является модель ProductsSettingsModel
    updateSettings(ProductsSettingsModel $productsSettings);

Методы, доступные в сервисе talks

  1. close
    1. model (TalkCloseActionModel) - модель для закрытия беседы
    2. Результатом выполнения - является закрытие беседы или запуск NPS-бота для последующего закрытия беседы
    close(TalkCloseActionModel $closeAction)

Методы, доступные в сервисе files

  1. uploadOne
    1. model (FileUploadModel) - модель файла для загрузки
    2. Результатом выполнения является модель FileModel
    uploadOne(FileUploadModel $model);

Методы, доступные в сервисе websiteButtons

  1. getOne - получить 1 сущность:
    1. id (int|string) - id источника
    2. with (array) - массив параметров with, которые поддерживает модель сервиса
    3. Результатом выполнения будет модель сущности WebsiteButtonModel
    getOne($id, array $with => []);
  2. get - получить несколько сущностей:
    1. filter (BaseEntityFilter) - фильтр для сущности
    2. with (array) - массив параметров with, которые поддерживает модель сервиса
    3. Результатом выполнения будет коллекция WebsiteButtonsCollection из сущностей WebsiteButtonModel
    get(BaseEntityFilter $filter = null, array $with = []);
  3. createAsync - добавить источник типа "кнопка на сайт"
    1. model (WebsiteButtonCreateRequestModel) - модель со свойствами:
      1. pipelineId (int) - id воронки
      2. trustedWebsites (array) - список доверенных адресов на которых будет размещена "кнопка на сайт". Например amocrm.ru, https://amocrm.ru
      3. isUsedInApp (true|false) - true, если кнопка встраивается в приложение, а не на сайт
    2. Результатом выполнения будет модель WebsiteButtonCreateResponseModel
    createAsync(WebsiteButtonCreateRequestModel $model);
  4. updateAsync - добавить дополнительные доверенные адреса
    1. model (WebsiteButtonUpdateRequestModel) - модель со свойствами:
      1. sourceId (int) - id источника
      2. trustedWebsitesToAdd (array) - список доверенных адресов на которых будет размещена "кнопка на сайт"
    2. Результатом выполнения будет модель WebsiteButtonModel
    updateAsync(WebsiteButtonUpdateRequestModel $model);
  5. addOnlineChatAsync - добавить канал связи "Онлайн-чат" к кнопке на сайт
    1. sourceId - id источника
    2. Результатом выполнения будет void значение
    addOnlineChatAsync(int $sourceId);

Обработка ошибок

Вызов методов библиотеки может выбрасывать ошибки типа AmoCRMApiException. В данные момент доступны следующие типы ошибок, они все наследуют AmoCRMApiException:

Тип Условия
AmoCRM\Exceptions\AmoCRMApiConnectExceptionException Подключение к серверу не было выполнено
AmoCRM\Exceptions\AmoCRMApiErrorResponseException Сервер вернул ошибку на выполняемый запрос
AmoCRM\Exceptions\AmoCRMApiHttpClientException Произошла ошибка http клиента
AmoCRM\Exceptions\AmoCRMApiNoContentException Сервер вернул код 204 без результата, ничего страшного не произошло, просто нет данных на ваш запрос
AmoCRM\Exceptions\AmoCRMApiTooManyRedirectsException Слишком много редиректов (в нормальном режиме не выкидывается)
AmoCRM\Exceptions\AmoCRMoAuthApiException Ошибка в oAuth клиенте
AmoCRM\Exceptions\BadTypeException Передан неверный тип данных
AmoCRM\Exceptions\InvalidArgumentException Передан неверный аргумент
AmoCRM\Exceptions\NotAvailableForActionException Метод не доступен для вызова
AmoCRM\Exceptions\AmoCRMApiPageNotAvailableException Выбрасывается в случае запроса следующей или предыдущей страницы коллекции, когда страница отсутствует
AmoCRM\Exceptions\AmoCRMMissedTokenException Не установлен Access Token для выполнения запроса

У выброшенных Exception есть следующие методы:

  1. getErrorCode()
  2. getTitle()
  3. getLastRequestInfo()
  4. getDescription()

У ошибки типа AmoCRMApiErrorResponseException есть метод getValidationErrors(), который вернет ошибки валидации входных данных.

Фильтры

В данный момент библиотека поддерживает фильтры для следующих сервисов:

Сервис Фильтр Особенности Поддерживает ли сортировку?
catalogElements \AmoCRM\Filters\CatalogElementsFilter Доступен в ограниченном виде, в будущих версиях будет расширен
companies \AmoCRM\Filters\CompaniesFilter Доступен только на аккаунтах, которые подключены к тестированию функционала фильтрации по API
contacts \AmoCRM\Filters\ContactsFilter Доступен только на аккаунтах, которые подключены к тестированию функционала фильтрации по API
customers \AmoCRM\Filters\CustomersFilter Доступен только на аккаунтах, которые подключены к тестированию функционала фильтрации по API
customFields \AmoCRM\Filters\CustomFieldsFilter Фильтр для метода получения дополнительных полей \AmoCRM\EntitiesServices\CustomFields::get
leads \AmoCRM\Filters\LeadsFilter Доступен только на аккаунтах, которые подключены к тестированию функционала фильтрации по API
events \AmoCRM\Filters\EventsFilter Фильтр для списка событий
leads, contacts, customers, companies \AmoCRM\Filters\LinksFilter Фильтр для получения связей для метода \AmoCRM\EntitiesServices\HasLinkMethodInterface::getLinks
notes \AmoCRM\Filters\NotesFilter Фильтра для \AmoCRM\EntitiesServices\EntityNotes::get
tags \AmoCRM\Filters\TagsFilter Фильтр для \AmoCRM\EntitiesServices\EntityTags::get
tasks \AmoCRM\Filters\TasksFilter Фильтр для метода \AmoCRM\EntitiesServices\Tasks::get
unsorted \AmoCRM\Filters\UnsortedFilter Фильтр для метода \AmoCRM\EntitiesServices\Unsorted::get
unsorted \AmoCRM\Filters\UnsortedSummaryFilter Фильтр для метода \AmoCRM\EntitiesServices\Unsorted::summary
webhooks \AmoCRM\Filters\WebhooksFilter Фильтр для метода получения хуков
sources \AmoCRM\Filters\SourcesFilter Фильтр для метода получения источников \AmoCRM\EntitiesServices\Sources::get
chatTemplates \AmoCRM\Filters\Chats\TemplatesFilter Фильтр для метода получения шаблонов чатов \AmoCRM\EntitiesServices\Chats\Templates::get
Сервисы, где необходима постраничная навигация \AmoCRM\Filters\PagesFilter Фильтр, который подходит для любого сервиса, где есть постраничная навигация

Работа с дополнительными полями сущностей

Дополнительные поля доступны у сущностей следующих сервисов:

  1. leads
  2. contacts
  3. companies
  4. customers
  5. catalogElements
  6. segments

У моделей, которые возвращаются этими сервисами, поля можно получить через метод getCustomFieldsValues(). На вызов данного метода возвращается объект CustomFieldsValuesCollection или null, если значений полей нет.

Внутри коллекции CustomFieldsValuesCollection находятся модели значений полей, все модели наследуются от BaseCustomFieldValuesModel, но зависят от типа поля.

У моделей, наследующих BaseCustomFieldValuesModel доступны следующие методы:

  1. getFieldId, setFieldId - получение/установка id поля
  2. getFieldType - получение типа поля
  3. getFieldCode, setFieldCode - получение/установка кода поля
  4. getFieldName, setFieldName - получение/установка названия поля
  5. getValues, setValues - получение/установка коллекции значений

Так как некоторые поля могут иметь несколько значений, в свойстве values хранится именно коллекция значений типа BaseCustomFieldValueCollection. Моделями коллекции являются модели типа BaseCustomFieldValueModel.

Схема отношений объектов:

CustomFieldsValuesCollection 1 <---> n BaseCustomFieldValuesModel

BaseCustomFieldValuesModel::getValues() 1 <---> 1 BaseCustomFieldValueCollection

BaseCustomFieldValueCollection 1 <---> n BaseCustomFieldValueModel

Для разных типов полей мы уже подготовили разные модели и коллекции:

Namespace, в котором находятся модели значения - \AmoCRM\Models\CustomFieldsValues\ValueModels

Namespace, в котором находятся коллекции моделей значения - \AmoCRM\Models\CustomFieldsValues\ValueCollections

Namespace, в котором находятся модели дополнительных полей - \AmoCRM\Models\CustomFieldsValues

Тип поля Модель значения Коллекция моделей значений Модель доп поля Контакт Сделка Компания Покупатель Каталог Сегмент
Текст TextCustomFieldValueModel TextCustomFieldValueCollection TextCustomFieldValuesModel
Число NumericCustomFieldValueModel NumericCustomFieldValueCollection NumericCustomFieldValuesModel
Флаг CheckboxCustomFieldValueModel CheckboxCustomFieldValueCollection CheckboxCustomFieldValuesModel
Список SelectCustomFieldValueModel SelectCustomFieldValueCollection SelectCustomFieldValuesModel
Мультисписок MultiselectCustomFieldValueModel MultiselectCustomFieldValueCollection MultiSelectCustomFieldValuesModel
Мультитекст MultitextCustomFieldValueModel MultitextCustomFieldValueCollection MultitextCustomFieldValuesModel
Дата DateCustomFieldValueModel DateCustomFieldValueCollection DateCustomFieldValuesModel
Ссылка UrlCustomFieldValueModel UrlCustomFieldValueCollection UrlCustomFieldValuesModel
Дата и время DateTimeCustomFieldValueModel DateTimeCustomFieldValueCollection DateTimeCustomFieldValuesModel
Текстовая область TextareaCustomFieldValueModel TextareaCustomFieldValueCollection TextareaCustomFieldValuesModel
Переключатель RadiobuttonCustomFieldValueModel RadiobuttonCustomFieldValueCollection RadiobuttonCustomFieldValuesModel
Короткий адрес StreetAddressCustomFieldValueModel StreetAddressCustomFieldValueCollection StreetAddressCustomFieldValuesModel
Адрес SmartAddressCustomFieldValueModel SmartAddressCustomFieldValueCollection SmartAddressCustomFieldValuesModel
День рождения BirthdayCustomFieldValueModel BirthdayCustomFieldValueCollection BirthdayCustomFieldValuesModel
Юр. лицо LegalEntityCustomFieldValueModel LegalEntityCustomFieldValueCollection LegalEntityCustomFieldValuesModel
Цена PriceCustomFieldValueModel PriceCustomFieldValueCollection PriceCustomFieldValuesModel
Категория CategoryCustomFieldValueModel CategoryCustomFieldValueCollection CategoryCustomFieldValuesModel
Предметы ItemsCustomFieldValueModel ItemsCustomFieldValueCollection ItemsCustomFieldValuesModel
Метка TrackingDataCustomFieldValueModel TrackingDataCustomFieldValueCollection TrackingDataCustomFieldValuesModel
Связь с другим элементом LinkedEntityCustomFieldValueModel LinkedEntityCustomFieldValueCollection LinkedEntityCustomFieldValuesModel
Денежное MonetaryCustomFieldModel MonetaryCustomFieldValueCollection MonetaryCustomFieldValuesModel
Каталоги и списки ChainedListCustomFieldModel ChainedListCustomFieldValueCollection ChainedListCustomFieldValuesModel
Файл FileCustomFieldModel FileCustomFieldValueCollection FileCustomFieldValuesModel
Плательщик PayerCustomFieldModel PayerCustomFieldValueCollection PayerCustomFieldValuesModel ✅ (только счета-покупки)
Поставщик SupplierCustomFieldModel SupplierCustomFieldValueCollection SupplierCustomFieldValuesModel ✅ (только счета-покупки)

Пример кода, как создать коллекцию значения полей сущности:

//Создадим модель сущности
$lead = new LeadModel();
$lead->setId(1);
//Создадим коллекцию полей сущности
$leadCustomFieldsValues = new CustomFieldsValuesCollection();
//Создадим модель значений поля типа текст
$textCustomFieldValuesModel = new TextCustomFieldValuesModel();
//Укажем ID поля
$textCustomFieldValuesModel->setFieldId(123);
//Добавим значения
$textCustomFieldValuesModel->setValues(
    (new TextCustomFieldValueCollection())
        ->add((new TextCustomFieldValueModel())->setValue('Текст'))
);
//Добавим значение в коллекцию полей сущности
$leadCustomFieldsValues->add($textCustomFieldValuesModel);
//Установим в сущности эти поля
$lead->setCustomFieldsValues($leadCustomFieldsValues);

Чтобы удалить значения поля доступен специальный объект \AmoCRM\Models\CustomFieldsValues\ValueCollections\NullCustomFieldValueCollection.

Передав этот объект, вы зануляете значение поля.

Пример:

//Создадим модель сущности
$lead = new LeadModel();
$lead->setId(1);
//Создадим коллекцию полей сущности
$leadCustomFieldsValues = new CustomFieldsValuesCollection();
//Создадим модель значений поля типа текст
$textCustomFieldValuesModel = new TextCustomFieldValuesModel();
//Укажем ID поля
$textCustomFieldValuesModel->setFieldId(123);
//Обнулим значения
$textCustomFieldValuesModel->setValues(
    (new NullCustomFieldValueCollection())
);
//Добавим значение в коллекцию полей сущности
$leadCustomFieldsValues->add($textCustomFieldValuesModel);
//Установим сущности эти поля
$lead->setCustomFieldsValues($leadCustomFieldsValues);

Работа с тегами сущностей

Теги доступны как отдельный сервис tags. При создании данного сервиса, вы указываете тип сущности, с тегами которой вы будете работать.

В данный момент доступны:

  1. EntityTypesInterface::LEADS,
  2. EntityTypesInterface::CONTACTS,
  3. EntityTypesInterface::COMPANIES,
  4. EntityTypesInterface::CUSTOMERS,

Для работы с тегами конкретной сущности, нужно взаимодействовать с конкретной моделью сущности. С помощью методов getTags и setTags вы можете получить коллекцию тегов сущности или установить её.

Для изменения тегов вам необходимо передавать всю коллекцию тегов, иначе теги могут быть потеряны.

Пример добавления/изменения тегов у сущности:

//Создадим модель сущности
$lead = new LeadModel();
$lead->setId(1);
//Создадим коллекцию тегов с тегами и установим их в сущности
$lead->setTags((new TagsCollection())
    ->add(
        (new TagModel())
            ->setName('тег')
    )->add(
        (new TagModel())
            ->setId(123123)
    )
);

или

//Создадим модель сущности
$lead = new LeadModel();
$lead->setId(1);
//Создадим коллекцию тегов с тегами и установим их в сущности
$lead->setTags(
    TagsCollection::fromArray([
        [
            'name' => 'тег',
        ],
        [
            'id' => 123,
        ],
    ])
);

Для удаления тегов в setTags можно передать в setTags специальный объект \AmoCRM\Collections\NullTagsCollection.

Пример удаления тегов у сущности:

//Создадим модель сущности
$lead = new LeadModel();
$lead->setId(1);
//Удалим теги
$lead->setTags((new NullTagsCollection()));

Особенности работы с источниками

На данный момент источники созданные интеграциями учитываются только при создании неразобранного из чатов.

При добавлении источника обязательными полями являются external_id, name интеграция может создать в аккаунте не более 50 активных источников на аккаунт. При удалении источника, к примеру, со значением external_id: 'sales' и при повторном создании с тем же external_id crm может вернуть id раннее удаленного источника. Поэтому не стоит на стороне интеграции формировать первичный ключ из поля id.

Чтобы источник отображался в кнопке whatsapp CRM Plugin необходимо указать поле источника services с таким содержимым:

 [
   {
      "type": "whatsapp",
      "pages": [
         {
            "id": "<идентификатор или номер телефона>",
            "name": "My WhatsApp",
            "link": "<номер телефона>"
         }
      ]
   }
]

Чтобы правильно сформировать поле services можно воспользоваться моделью \AmoCRM\Collections\Sources\SourceServicesCollection

Источник по-умолчанию

Источник по-умолчанию (с полем default=true) может быть только один или отсутствовать совсем. При отсутствии источника по-умолчанию в сделках будет указан источник АПИ-интеграция с названием интеграции (как при создании неразобранного через АПИ).

Чтобы сменить источник по-умолчанию, достаточно нужному источнику проставить поле default=true, у предыдущего источника поле default будет выставлено в default=false. Но при удалении источника по-умолчанию интеграция сама должна указать новый источник по-умолчанию.

Миграция интеграции на множественные источники (для интеграций с чатами)

Источник по-умолчанию может быть использован интеграцией при переходе на множественные источники, особенно если интеграция поддерживает опцию написать первым.

К примеру исходное состояние:
Есть аккаунт с подключенной интеграцией с чатами, эта интеграция поддерживает только 1 источник. На данный момент нам не важно как была установлена интеграция: через DP или маркетплейс.

Интеграция начинает внедрение поддержки множественных источников, логически разобьем на этапы:

1 этап
Интеграция умеет работать с АПИ источниками (но не отправляет и не принимает источник в сообщениях amojo). Добавляет источник по-умолчанию, который логически соответствует источнику, использовавшемуся когда не было поддержки нескольких источников. Теперь crm будет присылать в сообщениях external_id этого источника для всех чатов которые явно не закреплены за конкретным источником.

2 этап
Интеграция умеет работать с источниками и при отправке сообщений от клиента (при создании чата) указывает external_id Все чаты с новыми сообщениями становятся размеченными по источнику.

Так же интеграция теперь обрабатывает источник и учитывает его при отправке сообщения от менеджера, в том числе при начале чата с клиентом (опция "написать первым").

3 этап
Интеграция позволяет администратору аккаунта добавить (через интеграцию) второй и последующие источники. Вся переписка числится за каким-то источником

Важный момент Источник по-умолчанию не привязывается к чатам, если его явно не передавали с сообщениями и тогда при смене источника по-умолчанию чат без разметки будет "числиться" за новым источником

Константы

Основные константы находятся в интерфейсе \AmoCRM\Helpers\EntityTypesInterface.

Также доступны константы в следующих классах/интерфейсах:

  1. \AmoCRM\OAuth\AmoCRMOAuth::BUTTON_COLORS - доступные цвета для кнопки на сайт
  2. \AmoCRM\Models\Unsorted\BaseUnsortedModel - константы для кодов категорий неразобранного
  3. \AmoCRM\Models\CustomFields\BirthdayCustomFieldModel - константы для свойства remind у поля День Рождения
  4. \AmoCRM\Models\Interfaces\CallInterface - константы статусов звонков
  5. \AmoCRM\EntitiesServices\Interfaces\HasParentEntity - константы для ключей в запросах методов, у которых есть родительский сущность (в данный момент только notes)
  6. \AmoCRM\Models\CustomFieldsValues\ValueModels\ItemsCustomFieldValueModel - константы для ключей значения поля Items
  7. \AmoCRM\Models\Rights\RightModel - константы, связанные с правами
  8. \AmoCRM\Models\AccountModel - константы для аргумента with для сервиса account
  9. \AmoCRM\Models\TaskModel - константы для дефолтных типов задач
  10. \AmoCRM\Models\NoteType\TargetingNote - константы поддерживаемых внешних сервисов для примечаний о таргетинге (добавляют DP)
  11. \AmoCRM\Models\RoleModel - константы для аргумента with для сервиса roles
  12. \AmoCRM\Models\Factories\NoteFactory - константы типов примечаний
  13. \AmoCRM\Models\NoteType\MessageCashierNote - статусы примечания "Сообщение кассиру"
  14. \AmoCRM\Models\LeadModel - константы для аргумента with для сервиса leads
  15. \AmoCRM\Filters\Interfaces\HasOrderInterface - константы для сортировки
  16. \AmoCRM\Models\EventModel - константы для аргумента with для сервиса events
  17. \AmoCRM\Models\CustomFields\CustomFieldModel - константы типов полей
  18. \AmoCRM\Models\Customers\CustomerModel - константы для аргумента with для сервиса customers
  19. \AmoCRM\Models\ContactModel - константы для аргумента with для сервиса contacts
  20. \AmoCRM\Models\CompanyModel - константы для аргумента with для сервиса companies
  21. \AmoCRM\Models\CatalogElementModel - константы для аргумента with для сервиса catalogElements
  22. \AmoCRM\Enum\InvoicesCustomFieldsEnums - константы для работы с полями каталога счетов (с версии 0.12 константы статусов переехали в \AmoCRM\Enum\Invoices\BillStatusEnumCode)
  23. \AmoCRM\Enum\Chats\Templates\Buttons\ButtonsEnums - типы кнопок шаблонов чатов
  24. \AmoCRM\Enum\Sources\SourceServiceTypeEnum - типы сервисов для источников
  25. \AmoCRM\Enum\Tags\TagColorsEnum - возможные цвета для тегов
  26. \AmoCRM\Enum\Invoices\BillStatusEnumCode - предустановленные статусы для Счетов/Покупок
  27. \AmoCRM\Enum\SuppliersCustomFieldsEnums - константы для свойств поля поставщик

Работа в случае смены субдомена аккаунта

/**
 * Получим модель с информацией о домене аккаунта по access_token
 * Подробнее: @see AccountDomainModel
 *
 * Запрос уходит на www.amocrm.ru/oauth2/account/subdomain
 * С Authorization: Bearer {access_token}
 * curl 'https://www.amocrm.ru/oauth2/account/subdomain' -H 'Authorization: Bearer {access_token}'
 *
 * @example examples/get_account_subdomain.php
 */
$accountDomain = $apiClient->getOAuthClient()
        ->getAccountDomain($accessToken);

// Возьмём из полученной модели текущий subdomain аккаунта и засетим наш апи клиент
$apiClient->setAccountBaseDomain($accountDomain->getSubdomain());
// ... дальше продолжаем работу с апи клиентом

Одноразовые токены интеграций, расшифровка

// Как пример, получим заголовки с реквеста
// И получим нужный нам X-Auth-Token
$token = $_SERVER['HTTP_X_AUTH_TOKEN'];

try {
    /**
     * Одноразовый токен для интеграций, для того чтобы его получить используйте
     * метод this.$authorizedAjax() в своей интеграции
     * Подробнее: @link https://www.amocrm.ru/developers/content/web_sdk/mechanics
     *
     * Данный токен должен передаваться в заголовках вместе с запросом на ваш удаленный сервер
     * X-Auth-Token: {disposable_token}
     * Время жизни токена: 30 минут
     *
     * Расшифруем пришедший токен и получим модель с информацией
     * Подробнее: @see DisposableTokenModel
     */
    $disposableTokenModel = $apiClient->getOAuthClient()
        ->parseDisposableToken($token);

    var_dump($disposableTokenModel->toArray());
} catch (DisposableTokenExpiredException $e) {
    // Время жизни токена истекло
    printError($e);
    die;
} catch (DisposableTokenInvalidDestinationException $e) {
    // Не прошёл проверку на адресата токена
    printError($e);
    die;
} catch (DisposableTokenVerificationFailedException $e) {
    // Токен не прошел проверку подписи
    printError($e);
    die;
}

Также вы можете распарсить и модель одноразового токена для Salesbot/Marketingbot. Для этого необходимо сделать вызов метода parseBotDisposableToken:

$token = 'XXX';
try {
    /**
     * Одноразовый токен для ботов, его вы можете получить, сделав вызов widget_request в виджете в боте
     * Подробнее: @link https://www.amocrm.ru/developers/content/digital_pipeline/salesbot#handler-widget_request
     *
     * Данный токен содержит в себе информацию об аккаунте и о сущности, с которой работает бот
     * Для продолжения бота необходимо сделать запрос на метод, который был получен в теле хука
     * Подробнее: @link https://www.amocrm.ru/developers/content/crm_platform/widgets-api#widget-continue  
     *
     * Расшифруем пришедший токен и получим модель с информацией
     * Подробнее: @see BotDisposableTokenModel
     */
    $botDisposableTokenModel = $apiClient->getOAuthClient()
        ->parseBotDisposableToken($token);

    var_dump($botDisposableTokenModel->toArray());
} catch (DisposableTokenExpiredException $e) {
    // Время жизни токена истекло
    printError($e);
    die;
} catch (DisposableTokenInvalidDestinationException $e) {
    // Не прошёл проверку на адресата токена
    printError($e);
    die;
} catch (DisposableTokenVerificationFailedException $e) {
    // Токен не прошел проверку подписи
    printError($e);
    die;
}

Работа с валютами

/** @var AmoCRMApiClient $apiClient */

# Получим сервис для работы с валютами
$service = $apiClient->currencies();

# Получение списка валют
try {
    $collection = $service->get();
    var_dump($collection);
} catch (AmoCRMApiException $e) {
    printError($e);
    die;
}

# Получение списка валют с фильтром
$filter = new CurrenciesFilter();
$filter->setLimit(50);
$filter->setPage(2);

try {
    $collection = $service->get($filter);
    var_dump($collection);
} catch (AmoCRMApiException $e) {
    printError($e);
    die;
}

Примеры

В рамках данного репозитория имеется папка examples с различными примерами.

Для их работы необходимо добавить в неё файл .env со следующим содержимым, указав ваши значения:

CLIENT_ID="UUID интеграци"
CLIENT_SECRET="Секретный ключ интеграции"
CLIENT_REDIRECT_URI="https://example.com/examples/get_token.php (Важно обратить внимание, что он должен содержать в себе точно тот адрес, который был указан при создании интеграции)"

Затем вы можете поднять локальный сервер командой composer serve. После конфигурации необходимо перейти в браузере на страницу http://localhost:8181/examples/get_token.php для получения Access Token. Для получения доступа к вашему локальному серверу извне можно использовать сервис ngrok.io.

После авторизации вы можете проверить работу примеров, обращаясь к ним из браузера. Стоит отметить, что для корректной работы примеров необходимо проверить ID сущностей в них.

Работа с Issues

Если вы столкнулись с проблемой при работе с библиотекой, вы можете составить Issue, который будет рассмотрен при первой возможности.

При составлении, детально опишите проблему, приложите примеры кода, а также ответы на запросы из getLastRequestInfo.

Не забывайте удалять из примеров значимые данные, которые не должны стать достоянием общественности.

Также могут быть рассмотрены пожелания по улучшению библиотеки.

Вы можете предложить свои исправления/изменения исходного кода библиотеки, посредством создания Issue с описанием, а также Pull request с упоминанием Issue в комментарии к нему. Они будут рассмотрены и будут приняты или отклонены. Некоторые Pull Request могут остаться без ответа и действия, в случае, если правки потенциально жизнеспособны, но в данный момент не являются ключевыми для проекта.

Если вы столкнулись с проблемой функционала amoCRM - обратитесь в техническую поддержку через чат в вашем аккаунте.

License

MIT

amocrm-api-php's People

Contributors

amodmax avatar bessudnov avatar bigperson avatar bmoiseenko avatar bolid1 avatar catsys avatar constantable avatar dmitryberdnikov avatar dnklbgn avatar dotroo avatar dvishnevskij avatar kitos7 avatar lmcsu avatar m4tlch avatar magnum79 avatar makeroi avatar max-kut avatar mbelostropov avatar mvsvolkov avatar ndhfs avatar noweasily avatar nzhirkovamo avatar olapsema avatar rnixik avatar sawered avatar sevaktorosyan avatar soul-rise avatar vadimkorchagin avatar wischerdson avatar zvookiejoo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

amocrm-api-php's Issues

Упростить работу с получением связей для конкретной сущности

Пример в продолжение #63

Если нужно получить все контакты, связанные со сделкой, приходится использовать getLinks и перебирать результаты, чтобы отобрать связи нужного типа. Для LinksFilter нет возможности указать только EntityType. Было бы неплохо добавить такую возможность.

Если же использовать getContacts для LeadModel, то не удается получить контакты (из-за направления связи?).

Кнопка не прорисовывается (AmoCRM\Exceptions\BadTypeException: Invalid color selected)

Пример кода для прорисовки кнопки не рабочий. Код выбрасывает исключение BadTypeException
Fatal error: Uncaught AmoCRM\Exceptions\BadTypeException: Invalid color selected in ... vendor\amocrm\amocrm-api-library\src\AmoCRM\OAuth\AmoCRMOAuth.php:231
, так как в условиях
if (isset($options['color']) && !in_array($options['color'], self::BUTTON_COLORS, true))
in_array нужно заменить на array_key_exists:
if (isset($options['color']) && !array_key_exists($options['color'], self::BUTTON_COLORS))

Проблема с фильтрами "Некорректный запрос: ..."

В чем проблема?

Исключение:
AmoCRM\OAuth2\Client\Provider\AmoCRMException: Некорректный запрос: В запросе отсутствует ряд параметров или параметры невалидны

Проблемный код:


$contactsFilterModel = new ContactsFilter();

$testFilterCustomFieldsValues = [
  40335 => 'Some Position'
];

$contactsFilterModel->setCustomFieldsValues($testFilterCustomFieldsValues);

try {
  $apiClient->contacts()->get($contactsFilterModel);
} catch ( AmoCRMApiException $e ) {
  var_dump($e);
}

Method 'calls' error: "Entity not found"

Thank you for this nice AmoCRM library. I'm trying to use 'calls' following the example/calls.php method with request:

...
$call
    ->setPhone('+37067052156')
    ->setCallStatus(CallInterface::CALL_STATUS_SUCCESS_CONVERSATION)
    ->setCallResult('Разговор состоялся')
    ->setDuration(148)
    ->setUniq(Uuid::uuid4())
    ->setSource('integrationName')
    ->setDirection(CallInterface::CALL_DIRECTION_IN)
    ->setLink('https://example.test/test.mp3')
    ->setRequestId(0);
...

Got exception:

AmoCRM\Exceptions\AmoCRMApiException^
    #errorCode: 400
  #errorCode: 400
  #description: ""
  #title: "Invalid http status"
  #lastRequestInfo: array:10 [
    "last_http_method" => "POST"
    "last_method" => "https://testuojutelia.amocrm.ru/api/v4/calls"
    "last_body" => array:1 [
      0 => array:9 [
        "uniq" => "b3429754-faf8-4ff1-b797-4c92687861b4"
        "duration" => 148
        "source" => "integrationName"
        "link" => "https://example.test/test.mp3"
        "phone" => "+37067052156"
        "call_result" => "Разговор состоялся"
        "call_status" => 4
        "direction" => "inbound"
        "request_id" => "0"
      ]
    ]
    "last_query_params" => []
    "last_response" => """
      {\n
          "_total_items": 0,\n
          "errors": [\n
              {\n
                  "title": "Entity not found",\n
                  "status": 263,\n
                  "detail": "Entity not found",\n
                  "request_id": "0"\n
              }\n
...

Проблема с добавлением сделки вместе с тегами

Пытаюсь добавить сделку с тегами, делаю по примеру из этого файла

Выяснилось, что сервер не хочет принимать запрос на добавление, когда видит поле request_id у тега. Текст ошибки:

Response has validation errors

Вот отрывок из getLastRequestInfo() исключения:

{"validation-errors":[{"request_id":"0","errors":[{"code":"FieldNotExpected","path":"_embedded.tags.0.request_id","detail":"This field was not expected."},{"code":"FieldNotExpected","path":"_embedded.tags.1.request_id","detail":"This field was not expected."}]}],"title":"Bad Request","type":"https://httpstatus.es/400","status":400,"detail":"Request validation failed"}

Вот пример кода на добавление:

$tags = $apiClient->tags(EntityTypesInterface::LEADS)->get((new TagsFilter())->setQuery($tagName));
if (!is_null($tags)) {
    $tagsCollection->add($tags->getBy("name", $tagName));
} else {
    $newTag = new TagModel();     
    $newTag->setName($tagName);
    $newTag = $apiClient->tags(EntityTypesInterface::LEADS)->addOne($newTag);
    $tagsCollection->add($newTag);
}

...

$newLead = new LeadModel();
$newLead->setName($leadName)->setTags($tagsCollection);
return $leadsService->addOne($newLead)->getId();

Bad Request (400) код 244 при совершении POST запроса на "/api/v4/leads"

Начиная с 22.06.2020, при попытке добавить сделку получаю в качестве ответа ошибку Bad Request (400) с кодом 244. При работе с другими методами API или совершении GET запроса на "/api/v4/leads" проблем не наблюдается

'last_response' => '{"title":"Bad Request","type":"https://httpstatus.es/400","status":400,"detail":"Код ошибки 244. В случае повторного возникновения ошибки, обращайтесь в нашу техническую поддержку - [email protected]"}',
'last_response_code' => 400,
'last_request_id' => '8522c111dfbcacb99b9a2bc22ee07820'

Упростить работу с доп полями

Сейчас для работы с доп полями необходимо выстраивать достаточно сложную иерархию объектов.

Необходимо придумать и реализовать, как можно упростить эту работы, вероятно спрятать часть этой иерархии под капот.

Ожидается что-то подобное, а может быть и проще

$lead->getCustomFieldsValues()->getBy('name', 'поле')->getValue();

$customFieldsValuesCollection = new CustomFieldsValuesCollection();
$cfv = new CustomFieldValue();
$cfv
    ->setId(123)
    ->setValue(123);

$customFieldsValuesCollection->add($cfv);
$lead->setCustomFieldsValues($cfv);

$apiClient-users()->get() возвращает не все поля

При получении пользователей возвращаются юзеры без полей:
is_free, is_admin, is_active, group_id, role_id

Решение:
В классе RightModel:

    /**
     * @var bool|null
     */
    protected $isAdmin;

    /**
     * @var bool|null
     */
    protected $isActive;


    /**
     * @return bool|null
     */
    public function getIsAdmin(): ?bool
    {
        return $this->isAdmin;
    }

    /**
     * @param bool|null $isAdmin
     *
     * @return RightModel
     */
    public function setIsAdmin(?bool $isAdmin): RightModel
    {
        $this->isAdmin = $isAdmin;

        return $this;
    }

    /**
     * @return bool|null
     */
    public function getIsActive(): ?bool
    {
        return $this->isActive;
    }

    /**
     * @param bool|null $isActive
     *
     * @return RightModel
     */
    public function setIsActive(?bool $isActive): RightModel
    {
        $this->isActive = $isActive;

        return $this;
    }


    /**
     * @param array $rights
     *
     * @return self
     */
    public static function fromArray(array $rights): self
    {
        $model = new self();

        $model
            ->setCatalogAccess($rights['catalog_access'])
            ->setMailAccess($rights['mail_access'])
            ->setLeadsRights($rights['leads'])
            ->setContactsRights($rights['contacts'])
            ->setCompaniesRights($rights['companies'])
            ->setTasksRights($rights['tasks'])
            ->setIsAdmin($rights['is_admin'])
            ->setIsFree($rights['is_free'])
            ->setIsActive($rights['is_active'])
            ->setGroupId($rights['group_id'])
            ->setRoleId($rights['role_id']);

        if (!empty($rights['status_rights'])) {
            $model->setStatusRights($rights['status_rights']);
        }

        return $model;
    }


    /**
     * @inheritDoc
     */
    public function toArray(): array
    {
        $result = [
            'leads' => $this->getLeadsRights(),
            'contacts' => $this->getContactsRights(),
            'companies' => $this->getCompaniesRights(),
            'tasks' => $this->getTasksRights(),
            'status_rights' => $this->getStatusRights(),
            'mail_access' => $this->getMailAccess(),
            'catalog_access' => $this->getCatalogAccess(),
        ];

        if (!is_null($this->getRoleId())) {
            $result['role_id'] = $this->getRoleId();
        }

        if (!is_null($this->getIsFree())) {
            $result['is_free'] = $this->getIsFree();
        }

        if (!is_null($this->getIsAdmin())) {
            $result['is_admin'] = $this->getIsAdmin();
        }

        if (!is_null($this->getIsActive())) {
            $result['is_active'] = $this->getIsActive();
        }

        if (!is_null($this->getGroupId())) {
            $result['group_id'] = $this->getGroupId();
        }


        return $result;
    }


    public function toUsersApi(?string $requestId = null): array
    {
        $result = $this->toApi($requestId);

        if (!is_null($this->getRoleId())) {
            $result = [
                'role_id' => $this->getRoleId(),
            ];
        }

        if (!is_null($this->getGroupId())) {
            $result['group_id'] = $this->getGroupId();
        }

        if (!is_null($this->getIsFree())) {
            $result = [
                'is_free' => $this->getIsFree(),
            ];
        }

        if (!is_null($this->getIsAdmin())) {
            $result = [
                'is_admin' => $this->getIsAdmin(),
            ];
        }

        if (!is_null($this->getIsActive())) {
            $result = [
                'is_active' => $this->getIsActive(),
            ];
        }

        return $result;
    }

Ошибка при вызове CustomFieldsGroupService->get();

Вот какой кусок кода, обычный самый:

$cfgService = $user->getAmocrmApiClient()->customFieldGroups(EntityTypesInterface::LEADS);
$groups = $cfgService->get();

При этом вылезает ошибка

Exception 'Error' with message 'Cannot use object of type AmoCRM\Models\CustomFieldGroupModel as array'

in /Users/romanmazyrin/PhpstormProjects/mazdata/vendor/amocrm/amocrm-api-library/src/AmoCRM/Models/CustomFieldGroupModel.php:55

Переходим в файл CustomFieldGroupModel.php на строчку 55, и там кажется реально бага:

/**
     * @param array $customFieldGroup
     *
     * @return self
     */
    public static function fromArray(array $customFieldGroup): self
    {
        if (empty($customFieldGroup['id'])) {
            throw new InvalidArgumentException('Custom field group id is empty in ' . json_encode($customFieldGroup));
        }

        $customFieldGroupModel = new self();

        $customFieldGroupModel
            ->setId($customFieldGroup['id'])
            ->setName($customFieldGroup['name'])
            ->setSort($customFieldGroup['sort'])
            ->setIsPredefined($customFieldGroup['is_predefined'])
            ->setEntityType($customFieldGroupModel['entity_type']); // ВОТ ЭТО 55 СТРОКА.
//Тут должно быть $customFieldGroup, а не $customFieldGroupModel.
//Если поставить $customFieldGroup - ошибка исчезает.

        return $customFieldGroupModel;
    }

PHP 7.3 LeadsFilter::setLimit TypeError

Expected Behavior

Вызов setLimit устанавливает значение для свойства limit, возвращает текущий объект

Current Behavior

При попытке вызова метода setLimit у экземпляра класса LeadsFilter происходит ошибка

TypeError: Return value of AmoCRM\Filters\LeadsFilter::setLimit() must be an instance of AmoCRM\Filters\Traits\PagesFilterTrait, instance of AmoCRM\Filters\LeadsFilter returned in /.../amocrm/amocrm-api-library/src/AmoCRM/Filters/Traits/PagesFilterTrait.php:44

Environment

PHP 7.3

Ошибка при добавлении/обновлении значения доп. поля контакта типа Multitext

Current Behavior

Не удаётся добавить контакт с доп. полем типа multitex, не удается обновить значение доп. поля типа multitex (например, номер телефона контакта)

  1. Если в MultitextCustomFieldValueModel указано значение поля enum, при вызове метода добавления\обновления контакта апи возвращает ошибку
    {"validation-errors":[{"request_id":"0","errors":[{"code":"FieldNotExpected","path":"custom_fields_values.0.values.0.enum","detail":"This field was not expected."}]}],"title":"Bad Request","type":"https://httpstatus.es/400","status":400,"detail":"Request validation failed"}
  2. Если у MultitextCustomFieldValueModel попытаться не выставлять/обнулить значение поля enum, во время вызова Contacts->add/Contacts->update при подготовке данных для отправки к API в результате MultitextCustomFieldValueModel->toArray возникает ошибка
    TypeError: Return value of AmoCRM\Models\CustomFieldsValues\ValueModels\MultitextCustomFieldValueModel::getEnum() must be of the type string, null returned in /.../amocrm/amocrm-api-library/src/AmoCRM/Models/CustomFieldsValues/ValueModels/MultitextCustomFieldValueModel.php:22
  3. Если подправить тип поля enum со стринга на обнуляемый стринг, ошибки, описанной в пункте 2, удается избежать, однако в запросе на добавление/обновление контакта этот enum
    все равно передается со значением null, что вновь приводит к ошибке из пункта 1
  4. Если при сериализации убрать из запроса поле enum (оставить enum_id и value, как описано в документации в разделе "Редактирование контактов или компаний") - запрос выполняется без ошибки, только вот номер телефона не обновляется
  5. Если при сериализации убрать из запроса поля enum и enum_id, оставив только value, запрос выполняется без ошибок, телефон обновляется

Environment

PHP 7.3
amoCRM API Client 0.1.1

Как добавить дополнительное поле для неразобранного?

Доброго времени суток, подскажите, как добавить дополнительное поле в Lead для неразобранного?
Есть метод setLead() но он для модели LeadModel, а вот для контакта это возможно setContacts() с ContactsCollection

$unsortedLead = new LeadModel();
$leadCustomFields = new CustomFieldsValuesCollection();
$cidFieldValueModel = new TextCustomFieldValuesModel();
$cidFieldValueModel->setFieldId('453259');
$cidFieldValueModel->setValues(
     (new TextCustomFieldValueCollection())
          ->add((new TextCustomFieldValueModel())->setValue('test'))
 );
$leadCustomFields->add($cidFieldValueModel);
$unsortedLead->setName('Новая заявка от '.$name);
$unsortedLead->setCustomFieldsValues($leadCustomFields);

$formUnsorted = new FormUnsortedModel();
$formUnsorted
        ->setLead($unsortedLead)

Вариант выше не передает доп поле в сделку

Не работает DateCustomFieldValueModel помогите разобраться

$dateTo = '2020-08-23';
    if(!empty($dateTo)){
            $dateToFieldValueModel = new DateCustomFieldValuesModel();
            $dateToFieldValueModel->setFieldId(66213);
            $dateToFieldValueModel->setValues(
                (new DateCustomFieldValueCollection())
                    ->add((new DateCustomFieldValueModel())
                        ->setValue($dateTo)
                )
            );
            $leadCustomFields->add($dateToFieldValueModel);
        }
        if($leadCustomFields->count() > 0){
            $lead->setCustomFieldsValues($leadCustomFields);
        }
        
        try {
            $lead = $leadsService->addOne($lead);
...

падает и ввыдает ошибку:
Response has validation errors. Request validation failed

Ошибка в именах параметров

В классе AccountModel (строки 344-355)

if (isset($account[AmoCRMApiRequest::EMBEDDED][self::DATETIME_SETTINGS])) {
            $dateTimeSettings = $account[AmoCRMApiRequest::EMBEDDED][self::DATETIME_SETTINGS];
            $accountModel->setDatetimeSettings(new DateTimeSettings(
                $dateTimeSettings['date_pattern'],
                $dateTimeSettings['short_date_pattern'],
                $dateTimeSettings['short_time_pattern'],
                $dateTimeSettings['date_formant'],
                $dateTimeSettings['time_format'],
                $dateTimeSettings['timezone'],
                $dateTimeSettings['timezone_offset']
            ));
        }

заменить на

if (isset($account[AmoCRMApiRequest::EMBEDDED][self::DATETIME_SETTINGS])) {
            $dateTimeSettings = $account[AmoCRMApiRequest::EMBEDDED][self::DATETIME_SETTINGS];
            $accountModel->setDatetimeSettings(new DateTimeSettings(
                $dateTimeSettings['date_pattern'],
                $dateTimeSettings['short_date_pattern'],
                $dateTimeSettings['short_time_pattern'],
                $dateTimeSettings['date_format'],
                $dateTimeSettings['time_format'],
                $dateTimeSettings['timezone'],
                $dateTimeSettings['timezone_offset']
            ));
        }

ошибка в строке $dateTimeSettings['date_formant'],

Попытался связать контакт с новой сделкой. Где ошибся?

Текст ошибки:
Uncaught AmoCRM\Exceptions\AmoCRMApiErrorResponseException: Response has validation errors

Проблемный код:

$leadModel = $leadsAdapter->makeModel($testLead); // : LeadModel

$baseMainContactModel = $apiClient->contacts()->getOne(30246607);
$linksCollection = new LinksCollection();
$linksCollection->add($leadModel);

$apiClient->contacts()->link(
  $baseMainContactModel,
  $linksCollection
);

try {
  $apiClient->leads()->addOne($leadModel);
} catch( AmoCRMApiException $e ) {
  echo '<pre>';
  var_dump($e);
  echo '</pre>';
}

Как получить кастомные поля сущности по ID группы полей?

Пытаюсь получить список полей из группы (Основное) по ID группы, но поля как вижу не привязаны к группе (Основное, "default"). Вопрос как их получить через библиотеку?

Вот пример полученного поля, но "group_id" у него пустое, хоть amoCRM отображает его в группе "Основное"

[0]=>
    object(AmoCRM\Models\CustomFields\TextCustomFieldModel)#126 (15) {
      ["id":protected]=>
      int(40335)
      ["name":protected]=>
      string(18) "Должность"
      ["groupId":protected]=>
      NULL
      ["sort":protected]=>
      int(504)
      ["isApiOnly":protected]=>
      bool(false)
      ["isDeletable":protected]=>
      bool(false)
      ["isVisible":protected]=>
      NULL
      ["isRequired":protected]=>
      NULL
      ["catalogId":protected]=>
      NULL

Users->get приводит к RightModel::setStatusRights TypeError

Expected Behavior

Вызов метода get сервиса users возвращает коллекцию пользователей аккаунта

Current Behavior

При попытке получения списка пользователей с помощью вызова метода get у экземпляра класса AmoCRM\EntitiesServices\Users происходит ошибка

TypeError: Argument 1 passed to AmoCRM\Models\Rights\RightModel::setStatusRights() must be of the type array, null given, called in /.../amocrm/amocrm-api-library/src/AmoCRM/Models/Rights/RightModel.php on line 446 and defined in /.../amocrm/amocrm-api-library/src/AmoCRM/Models/Rights/RightModel.php:294
Stack trace:
0 /.../amocrm/amocrm-api-library/src/AmoCRM/Models/Rights/RightModel.php(446): AmoCRM\Models\Rights\RightModel->setStatusRights(NULL)
1 /.../amocrm/amocrm-api-library/src/AmoCRM/Models/UserModel.php(76): AmoCRM\Models\Rights\RightModel::fromArray(Array)
2 /.../amocrm/amocrm-api-library/src/AmoCRM/Collections/BaseApiCollection.php(53): AmoCRM\Models\UserModel::fromArray(Array)
3 [internal function]: AmoCRM\Collections\BaseApiCollection::AmoCRM\Collections{closure}(Array)
4 /.../amocrm/amocrm-api-library/src/AmoCRM/Collections/BaseApiCollection.php(50): array_map(Object(Closure), Array)
5 /.../amocrm/amocrm-api-library/src/AmoCRM/EntitiesServices/BaseEntity.php(103): AmoCRM\Collections\BaseApiCollection::fromArray(Array)
6 /.../amocrm/amocrm-api-library/src/AmoCRM/EntitiesServices/BaseEntity.php(89): AmoCRM\EntitiesServices\BaseEntity->createCollection(Array)
7 /...: AmoCRM\EntitiesServices\BaseEntity->get()

Environment

PHP 7.3
amoCRM API Client 0.1.1
в аккаунте один пользователь - создатель\администратор аккаунта

Possible sollution

В RightModel::fromArray добавить проверку на !empty($rights['status_rights']) перед вызовом setStatusRights

Ошибка при попытке вызова ContactModel->toArray()

Вот такой кусок кода:

$newContact = new ContactModel();
$newContact
    ->setName($name)
    ->setResponsibleUserId(123)
    ->setCustomFieldsValues($customFieldsCollection);
var_dump($newContact->toArray());

Возникает ошибка:

Exception 'TypeError' with message 'Return value of AmoCRM\Models\ContactModel::getCustomers() must be an instance of AmoCRM\Collections\Customers\CustomersCollection, null returned'

Вот stacktrace небольшой:

Exception 'TypeError' with message 'Return value of AmoCRM\Models\ContactModel::getCustomers() must be an instance of AmoCRM\Collections\Customers\CustomersCollection, null returned'

in /Users/romanmazyrin/PhpstormProjects/mazdata/vendor/amocrm/amocrm-api-library/src/AmoCRM/Models/ContactModel.php:452

Stack trace:
#0 /Users/romanmazyrin/PhpstormProjects/mazdata/vendor/amocrm/amocrm-api-library/src/AmoCRM/Models/ContactModel.php(618): AmoCRM\Models\ContactModel->getCustomers()
#1 /Users/romanmazyrin/PhpstormProjects/mazdata/models/AmocrmCustomIntegrations/DeltasalesIntegration.php(136): AmoCRM\Models\ContactModel->toArray()

Там очевидно, что интерпретатор ругается на возврат null вместо указанного типа, ведь при построении контакта поле customers я не трогаю и оно остается null.

Вот проблемный код:

Файл ContactModel.php, строка 452

/**
     * @return CustomersCollection
     */
    public function getCustomers(): CustomersCollection
    {
        return $this->customers;
    }

install laravel/framework v7.16.1

Здравствуйте.

Возникает ошибка при установке на с laravel выше версии 7.*, в composer.json "laravel/framework": "^7.0"
Сам текст ошибки
- don't install illuminate/support 5.5.x-dev|don't install laravel/framework v7.16.1

Заранее спасибо

Ошибка при добавлении DateCustomField

Если в АМО тип кастомного поля - дата, то при добавлении занчения таким образом

$dateCustomFieldValuesModel = new DateCustomFieldValuesModel();
        $dateCustomFieldValuesModel->setFieldId($fieldId);
        $dateCustomFieldValuesModel->setValues(
            (new DateCustomFieldValueCollection())
                ->add((new DateCustomFieldValueModel())->setValue('2020-06-17'))
        );
        $customFieldsValuesCollection->add($dateCustomFieldValuesModel);

возвращается ошибка
This value should be of type int
Приходится добавлять через NumericCustomField, передавая timestamp

Результат запроса при добавлении Неразобранного

Неразобранное можно добавить, получить, принять, отклонить и связать. При этом все методы кроме добавления Неразобранного возвращают результат выполнения операции, и только add() возвращает тот же массив, который отправлял, а очень хотелось бы в ответе видеть результат операции и идентификаторы созданных сущностей (контакты и сделка)

Получить контакт по кастом полю

Привет!

$FIELD_ID = 12345; // int ID, не name
$FIELD_VAL = 67890;
$filter = (new ContactsFilter())->setCustomFieldsValues([ $FIELD_ID => $FIELD_VAL ]);
$apiClient->contacts()->get($filter); // возвращаются неотфильтрованные контакты

Подскажите плз, что я делаю не так?
В примерах не нашел фильтра по кастом полям.

Также в ридми написано "ContactsFilter — Доступен только на аккаунтах, которые подключены к тестированию функционала фильтрации по API" — это актуально? (В техническую поддержку также написал)

Upd: поддержка ответила, что фильтрация включается по запросу, запросил включение. Вопрос с тем, как правильно фильтровать по кастом полям, актуален.

Invalid http status (405) при добавлении неразобранного

При использовании кода для добавления неразобранного

        $unsorted = new FormUnsortedModel();
        $unsorted->setSourceUid('vv_partner_reg');
        $unsorted->setSourceName('VV partner registration');
        $unsorted->setPipelineId(self::PIPELINE_ID_VV);

        $metadata = new FormsMetadata();
        $metadata->setFormId('vv_registration_form');
        $metadata->setFormName('vv registration form');
        $unsorted->setMetadata($metadata);

        /// `$contact` does not matter, data is in output
        $unsorted->setContacts(ContactsCollection::make([$contact]));

        $lead = new LeadModel();
        $lead->setPipelineId(self::PIPELINE_ID_VV);
        $lead->setName("Регистрация рекламодателя на VV №{$partner->id}");
        $unsorted->setLead($lead);

        try {
            $unsortedService = $this->apiClient->unsorted();
            $createdUnsorted = $unsortedService->addOne($unsorted);
        } catch (AmoCRMApiException $ex) {
            throw new AmoCrmException(
                $ex->getMessage() . ': ' . $ex->getCode() . ': ' . json_encode($ex->getLastRequestInfo()),
                $ex->getCode(),
                $ex
            );
        }

получаю исключение от api/v4/leads/unsorted:

Invalid http status: 405: {"last_http_method":"POST","last_method":"https://XXX.amocrm.ru/api/v4/leads/unsorted","last_body":[{"source_name":"VV partner registration","source_uid":"vv_partner_reg","pipeline_id":3388582,"metadata":{"form_id":"vv_registration_form","form_name":"vv registration form","form_page":null,"ip":null,"form_sent_at":1594357697,"referer":null,"visitor_uid":null},"_embedded":{"lead":{"name":"\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0440\u0435\u043a\u043b\u0430\u043c\u043e\u0434\u0430\u0442\u0435\u043b\u044f \u043d\u0430 VV \u21161","pipeline_id":3388582,"request_id":"0"},"contacts":[{"name":"XXXXX","custom_fields_values":[{"field_id":300041,"field_code":null,"values":[{"value":"XXXXXX","enum_id":428047,"enum_code":"WORK"}]},{"field_id":300039,"field_code":null,"values":[{"value":"XXXXXX","enum_id":428039,"enum_code":"MOB"}]}],"request_id":"0"}]},"request_id":"0"}],"last_query_params":[],"last_response":"","last_response_code":405,"last_request_id":"67ea12d844f921474c510308f4df0194","jquery_call":"\n$.ajax({\n url: "https://XXX.amocrm.ru/api/v4/leads/unsorted",\n data: JSON.stringify([{"source_name":"VV partner registration","source_uid":"vv_partner_reg","pipeline_id":3388582,"metadata":{"form_id":"vv_registration_form","form_name":"vv registration form","form_page":null,"ip":null,"form_sent_at":1594357697,"referer":null,"visitor_uid":null},"_embedded":{"lead":{"name":"\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f \u0440\u0435\u043a\u043b\u0430\u043c\u043e\u0434\u0430\u0442\u0435\u043b\u044f \u043d\u0430 VV \u21161","pipeline_id":3388582,"request_id":"0"},"contacts":[{"name":"xxx","custom_fields_values":[{"field_id":300041,"field_code":null,"values":[{"value":"xxx","enum_id":428047,"enum_code":"WORK"}]},{"field_id":300039,"field_code":null,"values":[{"value":"xxx","enum_id":428039,"enum_code":"MOB"}]}],"request_id":"0"}]},"request_id":"0"}]),\n type: "POST",\n contentType: "application/json"\n});","curl_call":"curl "https://xxx.amocrm.ru/api/v4/leads/unsorted" -X "POST" -d"0%5Bsource_name%5D=VV+partner+registration&0%5Bsource_uid%5D=vv_partner_reg&0%5Bpipeline_id%5D=3388582&0%5Bmetadata%5D%5Bform_id%5D=vv_registration_form&0%5Bmetadata%5D%5Bform_name%5D=vv+registration+form&0%5Bmetadata%5D%5Bform_sent_at%5D=1594357697&0%5B_embedded%5D%5Blead%5D%5Bname%5D=%D0%A0%D0%B5%D0%B3%D0%B8%D1%81%D1%82%D1%80%D0%B0%D1%86%D0%B8%D1%8F+%D1%80%D0%B5%D0%BA%D0%BB%D0%B0%D0%BC%D0%BE%D0%B4%D0%B0%D1%82%D0%B5%D0%BB%D1%8F+%D0%BD%D0%B0+VV+%E2%84%961&0%5B_embedded%5D%5Blead%5D%5Bpipeline_id%5D=3388582&0%5B_embedded%5D%5Blead%5D%5Brequest_id%5D=0&0%5B_embedded%5D%5Bcontacts%5D%5B0%5D%5Bname%5D=xxx&0%5B_embedded%5D%5Bcontacts%5D%5B0%5D%5Bcustom_fields_values%5D%5B0%5D%5Bfield_id%5D=300041&0%5B_embedded%5D%5Bcontacts%5D%5B0%5D%5Bcustom_fields_values%5D%5B0%5D%5Bvalues%5D%5B0%5D%5Bvalue%5D=xxx&0%5B_embedded%5D%5Bcontacts%5D%5B0%5D%5Bcustom_fields_values%5D%5B0%5D%5Bvalues%5D%5B0%5D%5Benum_id%5D=428047&0%5B_embedded%5D%5Bcontacts%5D%5B0%5D%5Bcustom_fields_values%5D%5B0%5D%5Bvalues%5D%5B0%5D%5Benum_code%5D=WORK&0%5B_embedded%5D%5Bcontacts%5D%5B0%5D%5Bcustom_fields_values%5D%5B1%5D%5Bfield_id%5D=300039&0%5B_embedded%5D%5Bcontacts%5D%5B0%5D%5Bcustom_fields_values%5D%5B1%5D%5Bvalues%5D%5B0%5D%5Bvalue%5D=%2Bxxx&0%5B_embedded%5D%5Bcontacts%5D%5B0%5D%5Bcustom_fields_values%5D%5B1%5D%5Bvalues%5D%5B0%5D%5Benum_id%5D=428039&0%5B_embedded%5D%5Bcontacts%5D%5B0%5D%5Bcustom_fields_values%5D%5B1%5D%5Bvalues%5D%5B0%5D%5Benum_code%5D=MOB&0%5B_embedded%5D%5Bcontacts%5D%5B0%5D%5Brequest_id%5D=0&0%5Brequest_id%5D=0"","timestamp":1594357698}

Контактные данные и домен заменены на xxx.

Неразобранное не создается.
При этом усешно удается отдельно создавать контакты и лиды.
В документации для двух других ресурсов 405 описано как неправильный метод.
Почему здесь получаю такой ответ?

"Response has validation errors" при установке "ответственного"

Пытаюсь создать контакт, возникла проблема с добавлением ответственного
Result: "Response has validation errors"

    try {

      $test = [
        'name' => 'New Contact',
        'responsible_user_id' => 25458031,
      ];

      $newContactModel = new ContactModel();
      $newContactModel->setName( $test['name'] );
      $newContactModel->setResponsibleUserId( $test['responsible_user_id'] );

      return $apiClient->contacts()->addOne($newContactModel);


    } catch( AmoCRMApiException $e ) {
      print( $e->getMessage() );
    }

класс /src/AmoCRM/Filters/TasksFilter не наследуется от BaseEntityFilter

На данный момент класс TasksFilter не наследуется от BaseEntityFilter из-за этого при попытке получения задач с использованием фильтра $apiClient->tasks()->get({объект TasksFilter}) будет падать ошибка, что ожидался BaseEntityFilter, а пришло TasksFilter

Пример кода:

$filter = new TasksFilter();
$filter->setIds([10592381]);
$task = $apiClient->tasks()->get($filter)->first();

При выполнении кода, скрипт упадет с ошибкой:

/test.php - Uncaught TypeError: Argument 1 passed to AmoCRM\EntitiesServices\BaseEntity::get() must be an instance of AmoCRM\Filters\BaseEntityFilter or null, instance of AmoCRM\Filters\TasksFilter given, called in /home/akurnosov/Документы/testAmoCRM/test.php on line 42 and defined in /home/akurnosov/Документы/testAmoCRM/vendor/amocrm/amocrm-api-library/src/AmoCRM/EntitiesServices/BaseEntity.php:76
Stack trace:
#0 /home/akurnosov/Документы/testAmoCRM/test.php(42): AmoCRM\EntitiesServices\BaseEntity->get(Object(AmoCRM\Filters\TasksFilter))
#1 {main}
  thrown in /home/akurnosov/Документы/testAmoCRM/vendor/amocrm/amocrm-api-library/src/AmoCRM/EntitiesServices/BaseEntity.php on line 76

Исправляется путем замены в /src/AmoCRM/Filters/TasksFilter

class TasksFilter на
class TasksFilter extends BaseEntityFilter implements HasPagesInterface

Ошибка при вызове метода getCurrent() у account

Вот основной текст ошибки: 'Argument 1 passed to AmoCRM\Models\AccountModel::setCreatedAt() must be an instance of Carbon\Carbon, integer given, called in...'

Вот небольшой кусок stacktrace:

Stack trace:
#0 /Users/romanmazyrin/PhpstormProjects/mazdata/vendor/amocrm/amocrm-api-library/src/AmoCRM/Models/AccountModel.php(280): AmoCRM\Models\AccountModel->setCreatedAt(1550501841)
#1 /Users/romanmazyrin/PhpstormProjects/mazdata/vendor/amocrm/amocrm-api-library/src/AmoCRM/EntitiesServices/Account.php(68): AmoCRM\Models\AccountModel::fromArray(Array)
#2 /Users/romanmazyrin/PhpstormProjects/mazdata/commands/HelloController.php(21): AmoCRM\EntitiesServices\Account->getCurrent(Array)

Проблема на лицо, то что передается в метод значение не того типа. Но проблема в том, что вчера работало нормально и не ругалось. Что за магия?

Ответ на запрос к серверу приходит нормальный, все ок.

Версия PHP 7.2.31, (cli)

Method 'calls' error: "path" => "request_id", "detail" => "This value should not be null."

Thank you for making this beautiful library. I'm working on AmoCRM 'add calls' integration. Using your library as you shown in examples/calls.php got error "path" => "request_id", "detail" => "This value should not be null.". The solution I ended up looking at CallModel() and at RequestIdTrait() is to add one more method ->setRequestId('myId') when creating $call object:

...
$call
    ->setPhone('+7912312321')
    ->setCallStatus(CallInterface::CALL_STATUS_SUCCESS_CONVERSATION)
    ->setCallResult('Разговор состоялся')
    ->setDuration(148)
    ->setUniq(Uuid::uuid4())
    ->setSource('integration name')
    ->setDirection(CallInterface::CALL_DIRECTION_IN)
    ->setLink('https://example.test/test.mp3')
    ->setRequestId('myId');
...

Laravel Framework 7.0, problem ramsey 4.x (amocrm/amocrm-api-library 0.3.1 requires ramsey/uuid 3.*)

Проблема при установке amoCRM API Library в проект на Laravel 7.0

 Problem 1                                                                                        
   - Installation request for amocrm/amocrm-api-library ^0.3.1 -> satisfiable by amocrm/amocrm-api
   - Conclusion: remove ramsey/uuid 4.0.1                                                         
   - Conclusion: don't install ramsey/uuid 4.0.1                                                  
   - amocrm/amocrm-api-library 0.3.1 requires ramsey/uuid 3.* -> satisfiable by ramsey/uuid[3.0.0,
0, 3.4.0, 3.4.1, 3.5.0, 3.5.1, 3.5.2, 3.6.0, 3.6.1, 3.7.0, 3.7.1, 3.7.2, 3.7.3, 3.8.0, 3.9.0, 3.9.
   - Can only install one of: ramsey/uuid[3.7.0, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.7.1, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.7.2, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.7.3, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.8.0, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.9.0, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.9.1, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.9.2, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.9.3, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.x-dev, 4.0.1].                                        
   - Can only install one of: ramsey/uuid[3.0.0, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.0.0-alpha1, 4.0.1].                                   
   - Can only install one of: ramsey/uuid[3.0.0-alpha2, 4.0.1].                                   
   - Can only install one of: ramsey/uuid[3.0.0-alpha3, 4.0.1].                                   
   - Can only install one of: ramsey/uuid[3.0.0-beta1, 4.0.1].                                    
   - Can only install one of: ramsey/uuid[3.0.1, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.1.0, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.2.0, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.3.0, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.4.0, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.4.1, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.5.0, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.5.1, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.5.2, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.6.0, 4.0.1].                                          
   - Can only install one of: ramsey/uuid[3.6.1, 4.0.1].                                          
   - Installation request for ramsey/uuid (locked at 4.0.1) -> satisfiable by ramsey/uuid[4.0.1]. 

При заполнении нескольких дополнительных полей, не корректно добавляются значения

При заполнении дополнительных полей, в коллекцию leadCustomFieldsValues добавляется несколько сущностей дополнительных полей, но при этом все они равны последней сущности. (Значения и id одинаковы)

$textCustomFieldValueModel = new TextCustomFieldValuesModel();
$leadCustomFieldsValues = new CustomFieldsValuesCollection();

$textCustomFieldValueModel->setFieldId('56309');
$textCustomFieldValueModel->setValues(
(new TextCustomFieldValueCollection())
->add((new TextCustomFieldValueModel())->setValue('132456'))
);
$leadCustomFieldsValues->add($textCustomFieldValueModel);

$textCustomFieldValueModel->setFieldId('58767');
$textCustomFieldValueModel->setValues(
(new TextCustomFieldValueCollection())
->add((new TextCustomFieldValueModel())->setValue('756'))
);
$leadCustomFieldsValues->add($textCustomFieldValueModel);

var_dump($leadCustomFieldsValues); die();

Ниже привожу var_dump созданной коллекции CustomField

object(AmoCRM\Collections\CustomFieldsValuesCollection)#141 (1) {
["data":protected]=>
array(2) {
[0]=>
object(AmoCRM\Models\CustomFieldsValues\TextCustomFieldValuesModel)#144 (4) {
["fieldId":protected]=>
int(58767)
["fieldCode":protected]=>
NULL
["fieldName":protected]=>
NULL
["values":protected]=>
object(AmoCRM\Models\CustomFieldsValues\ValueCollections\TextCustomFieldValueCollection)#133 (1) {
["data":protected]=>
array(1) {
[0]=>
object(AmoCRM\Models\CustomFieldsValues\ValueModels\TextCustomFieldValueModel)#134 (1) {
["value":protected]=>
string(3) "756"
}
}
}
}
[1]=>
object(AmoCRM\Models\CustomFieldsValues\TextCustomFieldValuesModel)#144 (4) {
["fieldId":protected]=>
int(58767)
["fieldCode":protected]=>
NULL
["fieldName":protected]=>
NULL
["values":protected]=>
object(AmoCRM\Models\CustomFieldsValues\ValueCollections\TextCustomFieldValueCollection)#133 (1) {
["data":protected]=>
array(1) {
[0]=>
object(AmoCRM\Models\CustomFieldsValues\ValueModels\TextCustomFieldValueModel)#134 (1) {
["value":protected]=>
string(3) "756"
}
}
}
}
}
}

Помогите решить данную проблему. Возможно это баг?

Проблема с получением покупателей в режиме "Сегментация"

Текст ошибки:

PHP Notice 'yii\base\ErrorException' with message 'Undefined index: name'

in /a/vendor/amocrm/amocrm-api-library/src/AmoCRM/Models/Customers/Segments/SegmentModel.php:118

Как воспроизвести ошибку:

  1. В аккаунте надо поставить режим покупателей в "Динамическое сегментирование".
  2. Создать сегмент с любым названием (например test).
  3. Создать покупателя.
  4. Привязать сегмент к покупателю.
  5. После этого выполнить следующий код:
$customerId = ""; // Put target customer id here
$apiClient->customers()->getOne($customerId);

Дополнительная информация:

Вот что выводит var_dump параметра $segment в методе fromArray класса SegmentModel:

Код:

/**
     * @param array $segment
     *
     * @return self
     */
    public static function fromArray(array $segment): self  // ЭТО СТРОКА 108
    {
        
        var_dump($segment); die();
        
        if (empty($segment['id'])) {
            throw new InvalidArgumentException('Segment id is empty in ' . json_encode($segment));
        }

        $segmentModel = new self();

Результат вывода:

array(2) {
  ["id"]=>
  int(81)
  ["_links"]=>
  array(1) {
    ["self"]=>
    array(1) {
      ["href"]=>
      string(61) "https://testdeltasales.amocrm.ru/api/v4/customers/segments/81"
    }
  }
}

В методах fromArray классов, унаследованных от NoteModel, игнорируется поле params

public function fromArray(array $note): NoteModel
{
$model = parent::fromArray($note);
if (isset($note['params']['uniq'])) {
$this->setUniq($note['params']['uniq']);
}
if (isset($note['params']['duration'])) {
$this->setDuration($note['params']['duration']);
}
if (isset($note['params']['source'])) {
$this->setSource($note['params']['source']);
}
if (isset($note['params']['link'])) {
$this->setLink($note['params']['link']);
}
if (isset($note['params']['phone'])) {
$this->setPhone($note['params']['phone']);
}
if (isset($note['params']['call_result'])) {
$this->setCallResult($note['params']['call_result']);
}
if (isset($note['params']['call_status'])) {
$this->setCallStatus($note['params']['call_status']);
}
return $model;
}

При вызове метода fromArray объекта класса CallNote (и прочих, унаследованных от NoteModel) создается новый экземпляр этого класса, который возвращается без каких либо изменений, поскольку в методе осуществляется обновление полей текущего объекта, а не вновь созданного. Это приводит к тому, что при вызове EntityNotes->get() поля элементов коллекции, отвечающие за хранение "дополнительной информации" примечаний, передаваемой в поле params, оказываются выставленными в null.

Invalid http status (500) при вызове метода "/api/v4/leads/unsorted"

Неработающий код

try {
  $request = $apiClient->getRequest();
  $queryResult = $request->get('/api/v4/leads/unsorted');
} catch( AmoCRMApiException $e ) {
   var_dump($request->getLastRequestInfo()); die;
}

Ответ:

array(10) { ["last_http_method"]=> string(3) "GET" ["last_method"]=> string(58) "https://asdasdasdasd.amocrm.ru//api/v4/leads/unsorted" ["last_body"]=> array(0) { } ["last_query_params"]=> array(0) { } ["last_response"]=> string(119) "{"title":"Internal Server Error","type":"https://httpstatus.es/500","status":500,"detail":"An unknown error occurred."}" ["last_response_code"]=> int(500) ["last_request_id"]=> string(32) "79c6ee4a79179bab5d275f0ce848b57b" ["jquery_call"]=> string(167) " $.ajax({ url: "https://asdasdasdasd.amocrm.ru//api/v4/leads/unsorted", data: JSON.stringify([]), type: "GET", contentType: "application/json" });" ["curl_call"]=> string(79) "curl "https://asdasdasdasd.amocrm.ru//api/v4/leads/unsorted" -X "GET" -d""" ["timestamp"]=> int(1591914903) }

Метод работает только с API ключом.

возможность добавлять вложенные категории одним запросом

сейчас, чтобы добавить многоуровневую вложенность в поле типа категория
нужно несколько раз делать запрос на изменение, используя айдишники вложенности из результата прошло запроса
нужно добавить возможность создавать многоуровневую вложенность одним запросом

Invalid http status (500) при вызове метода "/api/v4/users"

При попытке вызова метода get сервиса Users

$apiClient->users()->get();

происходит исключение Invalid http status (500)

'last_response' => '{"title":"Internal Server Error","type":"https://httpstatus.es/500","status":500,"detail":"An unknown error occurred."}', 
'last_response_code' => 500, 
'last_request_id' => '554647960af2bdf878016309d0d3e5fc'

Mistake in CallModel

In the \Models\CallModel.php you forgot implement HasIdInterface what which leads to a critical error.
image

EntityNotes->getOne возвращает NoteModel , игнорируя note_type и params

EnitiyNotes->getOne возвращает объекта класса NoteModel независимо от принадлежности примечания тому или иному типу.

$response = $this->request->get($this->getMethodWithParent($parentId, $id), $queryParams);
$class = static::ITEM_CLASS;
/** @var BaseApiModel $entity */
$entity = new $class();
$entity = !empty($response) ? $entity->fromArray($response) : null;

Хотелось бы, чтоб EnitiyNotes->getOne работал по аналогии с CustomFields->getOne - объект создавался с помощью соотв. фабрики (NoteFactory::createForType)

$response = $this->request->get($this->getMethod() . '/' . $id, $queryParams);
$collection = !empty($response) ? $this->collectionClass::fromArray([$response]) : null;
return !empty($response) ? $collection->first() : null;

Ошибка в webhooks()->get()

`public static function fromArray(array $webhook): self
{
$model = new self();

    $model->setId($webhook['id'])
        ->setDestination($webhook['name'])
        ->setAccountId($webhook['account_id'])
        ->setSettings($webhook['settings'])
        ->setCreatedBy($webhook['created_by'])
        ->setCreatedAt($webhook['created_at'])
        ->setUpdatedAt($webhook['updated_at'])
        ->setSort($webhook['sort'])
        ->setDisabled($webhook['disabled']);

    return $model;
}`

изменить на
`public static function fromArray(array $webhook): self
{
$model = new self();

    $model->setId($webhook['id'])
        ->setDestination($webhook['destination'])
        ->setAccountId($webhook['account_id'])
        ->setSettings($webhook['settings'])
        ->setCreatedBy($webhook['created_by'])
        ->setCreatedAt($webhook['created_at'])
        ->setUpdatedAt($webhook['updated_at'])
        ->setSort($webhook['sort'])
        ->setDisabled($webhook['disabled']);

    return $model;
}`

(Строка 66)

Ошибка в модели \AmoCRM\Models\Unsorted\BaseUnsortedModel

Запрос на добавление в Неразобранное формируется через метод toApi() модели \AmoCRM\Models\Unsorted\BaseUnsortedModel
В ней сущность заявки передаётся в параметре lead, хотя в документации API сказано, что сущность заявки передаётся в параметре leads.
В итоге при попытке отправить заявку через существующую модель, базирующуюся на BaseUnsortedModel заявка в Неразобранном создаётся, а вот сущность сделки - нет, из-за того, что отправляется в неверном параметре.

Если заменить в методе toApi() строку $result[AmoCRMApiRequest::EMBEDDED]['lead'] = $this->getLead()->toApi($requestId); на строку $result[AmoCRMApiRequest::EMBEDDED]['leads'][] = $this->getLead()->toApi($requestId); А так же в методе toArray() заменить $result['lead'] на $result['leads'], то всё работает и добавляется корректно

Argument 1 passed to MailMetadata::setMessageId() must be of the type array or null

При наличии у неразобранного метаданных в категории mail (почта), вызов метода MailMetadata::fromArray() приводит к ошибке, поскольку сеттеры у свойств messageId и contentSummary принимают в качестве аргумента ?array, а в ответе от API возвращаются int и string соответственно.

TypeError:
Argument 1 passed to AmoCRM\Models\Unsorted\MailMetadata::setMessageId() must be of the type array or null, int given, called in vendor/amocrm/amocrm-api-library/src/AmoCRM/Models/Unsorted/MailMetadata.php on line 54

"illuminate/support": "5.*"

Добрый день.

Из-за "устаревшей" зависимости не удается установить библиотеку в рамках Laravel 6.

"illuminate/support": "5.*"

Может имеет смысл проверить совместимость с более актуальными версиями "illuminate/support" и поправить composer.json?

illuminate/support": "^5.0 || ^6.0 или "illuminate/support": "^5.0 || ^6.0 || ^7.0"

Заранее спасибо.

Проблема с использованием коллекции тегов

Пытаюсь добавить в контакт коллекцию сделок, в итоге ни один из тегов не попадает в CRM, хотя поле 'tags' модели 'ContactModel' заполнено корректно. Сущность создается, так что исключение не выбрасывается.

Вот код:

      $newContactModel = new ContactModel();
      $newContactModel->setName( 'Some name' );

      $tagModel = new TagModel();
      $tagModel->setName( '123' );

      $tagModel2 = new TagModel();
      $tagModel2->setName( '123123' );

      $tagsCollection = new TagsCollection();
      $tagsCollection->add($tagModel);
      $tagsCollection->add($tagModel2);

      $newContactModel->setTags( $tagsCollection );

      $contactsCollection = new ContactsCollection();
      $contactsCollection->add($newContactModel);
      return $apiClient->contacts()->add($contactsCollection);

$newContact:

object(AmoCRM\Collections\ContactsCollection)#33 (3) { ["data":protected]=> array(1) { [0]=> object(AmoCRM\Models\ContactModel)#29 (20) { ["id":protected]=> int(30072279) ["name":protected]=> string(9) "Some name" ["firstName":protected]=> NULL ["lastName":protected]=> NULL ["responsibleUserId":protected]=> NULL ["groupId":protected]=> NULL ["createdBy":protected]=> NULL ["updatedBy":protected]=> NULL ["createdAt":protected]=> NULL ["updatedAt":protected]=> NULL ["closestTaskAt":protected]=> NULL ["accountId":protected]=> NULL ["tags":protected]=> object(AmoCRM\Collections\TagsCollection)#32 (3) { ["data":protected]=> array(2) { [0]=> object(AmoCRM\Models\TagModel)#30 (3) { ["id":protected]=> NULL ["name":protected]=> string(3) "123" ["requestId":protected]=> NULL } [1]=> object(AmoCRM\Models\TagModel)#31 (3) { ["id":protected]=> NULL ["name":protected]=> string(6) "123123" ["requestId":protected]=> NULL } } ["nextPageLink":"AmoCRM\Collections\TagsCollection":private]=> NULL ["prevPageLink":"AmoCRM\Collections\TagsCollection":private]=> NULL } ["customFieldsValues":protected]=> NULL ["isMain":protected]=> NULL ["company":protected]=> NULL ["leads":protected]=> NULL ["customers":protected]=> NULL ["catalogElementsLinks":protected]=> NULL ["requestId":protected]=> string(1) "0" } } ["nextPageLink":"AmoCRM\Collections\ContactsCollection":private]=> NULL ["prevPageLink":"AmoCRM\Collections\ContactsCollection":private]=> NULL }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.