API для работы с Грузами
Возможности API
API Грузов позволяет работам с Грузами. С помощью API:
Грузовладелец может:
- добавлять, обновлять, редактировать и архивировать общедоступные грузы и грузы на Площадках;
- настраивать приоритетный показ груза;
- получать встречные предложения по грузу.
Перевозчик может:
- узнавать о грузах компаний на Площадках (кроме Общей площадки ATI.SU);
- добавлять и редактировать встречные предложения;
- добавлять и редактировать комментарии к грузу.
Дубликаты и похожие грузы
Публикуемый груз проверяется на дубликаты — при добавлении, редактировании и восстановлении. Если такой груз уже опубликован, операция отклоняется с 409 Conflict и кодом duplicate_error; id и номер существующей заявки — в details ответа.
Кроме полных дубликатов Биржа находит и похожие грузы — заявки, совпадающие по основным параметрам перевозки, хоть и опубликованные как отдельные грузы. В поисковой выдаче похожие грузы могут схлопываться: вместо нескольких однотипных предложений перевозчик видит одно из них, и выдача остаётся разнообразной. Сам груз при этом публикуется как обычно — схлопывание касается только показа в результатах поиска.
Ставки НДС
В методах v2 безналичная ставка с НДС — это набор payment.rates_with_vat: массив объектов { rate_with_vat, vat_percent }. В наборе может быть несколько ставок с разными процентами НДС: одна цена для НДС 22%, другая для НДС 5%. Так перевозчики с разными системами налогообложения сразу видят свою цену.
rate_with_vat— сумма ставки;vat_percent— процент НДС целым числом.null— ставка «с НДС» без уточнения процента.
Набор дополняет остальные поля оплаты: rate_without_vat — безнал без НДС, cash — наличные. Хотя бы одна из ставок должна быть указана.
Прежнее одиночное поле rate_with_vat устарело — используйте rates_with_vat. В ответах методов оно теперь отдаёт максимальную ставку набора. Методы v1.0 принимают только поля SumWithNDS/SumWithoutNDS без процента НДС.
Доступные проценты по валютам
Список допустимых процентов зависит от валюты груза (currency_type). Его задаёт словарь валют: атрибут vat_percents элемента словаря — проценты строкой через запятую. У валют без НДС-процентов (доллар, евро и др.) атрибута нет — для них доступен только vat_percent: null. Метод v1.0 currencyTypes атрибут не отдаёт.
vat_percent: null доступен в любой валюте; такая ставка в наборе тоже одна.
Валидация набора ставок
| Правило | Код ошибки |
|---|---|
| Каждый процент набора есть в списке валюты груза | invalid_vat_percents |
| Один процент — одна ставка в наборе | invalid_vat_percents |
| Процент не отрицательный | invalid_rate |
| Сумма ставки в допустимых границах, максимум два знака после запятой | outbounds |
| Каждая ставка с НДС не меньше ставки без НДС | invalid_rate_without_vat |
| Указана хотя бы одна ставка: наличные, с НДС или без НДС | no_rates |
На нарушение методы v2 отвечают 400 с телом вида {"error_code": "validation_error", "reason": "…", "error_list": [{"property": "…", "reason": "…", "error": "…"}]}. Код из таблицы приходит в поле error элемента error_list.
Торги
В Торгах процент НДС — одно поле vat_percents: целое число от 1 до 99 рядом с флагами accept_bids_with_vat и accept_bids_without_vat. Когда включены оба флага, vat_percents обязателен. Со словарём валют это поле не сверяется.
У варианта завершения Торгов «опубликовать со ставкой» (no_winner_end_options) — собственный набор rates_with_vat с теми же полями, что и у обычной ставки.
Пользовательские ошибки в API грузов
В API грузов может произойти достаточно много пользовательских ошибок, и для идентификации конкретной ошибки не всегда достаточно http кода ошибки. Поэтому при возникновении ошибки в теле ответа всегда будет присутствовать объект ошибки, содержащий 2 поля: Error с кодом ошибки в виде строки и Reason с пояснением. В групповых операциях поля Error и Reason будут указаны для каждого груза, с которым произошла ошибка во время выполнения операции.
Примеры ошибок
Ошибка при одиночной операции (на примере добавления груза)
{ "Error": "load_conflict_error", "Reason": "Найден дубликат в грузах", "ConflictLoadId": "271be1d2-2a91-e611-a37f-005056c00008"}Ошибка при групповой операции (на примере группового восстановления)
{ "0e9050e4-f5cb-4835-acb2-c211151bad64": { "Status": 2, "Message": "Груз был помещен в архив менее 60 минут назад", "Error": "load_archive_delay_not_elapsed", "Reason": "Груз был помещен в архив менее 60 минут назад" }, "9b380991-433f-4738-a68d-8b851c1b5472": { "Status": 0, "Message": "Успешная операция" }}Ошибка валидации(на примере добавления груза)
В случае ошибки валидации json в теле запроса возникает ошибка json_validation_error. В теле ответа будут поля Error и Reason, и, кроме того, поле ErrorList, содержащее массив объектов вида {property;reason}, где:
property – название поля, в котором произошла ошибка;
reason – причина ошибки.
{ "Error": "json_validation_error", "Reason": "Ошибка валидации json", "ErrorsList": [ { "property": "Cargo.Weight", "reason": "Максимальная длина для параметра вес - 4 символа", "error": "length_out_of_range", "context": { "Min": 0, "Max": 4 } }, { "property": "FirstDate", "reason": "Если значение параметра DateType равно 0 или 2, допустимое значение параметра FirstDate - текущая дата", "error": "must_be_current_date" }, { "property": "LastDate", "reason": "Параметр LastDate должен быть больше либо равен параметру FirstDate", "error": "must_be_greater_or_equal_than", "context": { "Min": "2021-06-16T12:00:00.51" } } ]}Ошибка доступа (на примере добавления груза)
{ "Error": "access_denied_error", "Reason": "У вашего контакта нет доступа для работы с одной или несколькими персональными площадками, указанными в грузе", "AccessDeniedReason": 13, "ExceededLimit": null}Список возможных 4хх ошибок
| Код ошибки | Пояснение |
|---|---|
| deserialization_error | Ошибка десериализации json |
| json_validation_error | Ошибка валидации json из тела запроса |
| validation_error | Ошибка валидации. Возникает в случае любой ошибки валидации, кроме ошибки валидации тела запроса (json_validation_error) |
| load_conflict_error | Найден дубликат в грузах |
| load_archive_delay_not_elapsed | Груз был помещен в архив менее 60 минут назад |
| load_renew_delay_not_elapsed | Груз был обновлен менее 60 минут назад |
| boards_access_denied_error | Некоторые из указанных площадок не существуют или вы не имеете доступа к ним |
| access_denied_error | Отказано в доступе |
| account_not_found_error | Фирма не найдена |
| contact_not_found_or_deleted | Первый контакт не существует или удален |
| unavailable_contact_selected | В качестве первого контакта должен быть указан контакт из доступного подразделения |
| city_not_found_error | Город не найден |
| comment_not_found_error | Комментарий для груза не найден |
| dictionary_element_not_found_error | Элемент словаря не найден |
| load_not_found_error | Груз не найден |
| response_not_found_error | Отзыв на груз не найден |
| avatar_not_found_error | Аватар не найден |
| load_reserved_error | Груз зарезервирован/взят, операции с грузом запрещены |
| load_cant_reserve | Невозможно зарезервировать груз. Груз уже зарезервирован, либо не поддерживает резервирование |