Идеология сервиса

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

Центральным понятием в Смартомато является Организация (Organization) — зарегистрированный в системе ресторан, который обладает витриной и может иметь несколько филиалов, которые в терминах Смартомато называются Ресторанами (Restaurants). Каждый ресторан может иметь одну или несколько зон доставки, позволяющие задать особые условия доставки вокруг филиала. Кроме того, в организацию входят сотрудники (StaffMembers), выполняющие различные роли в бизнес-процессе (всегда есть хотя бы один сотрудник — администратор Организации). Организация также может иметь штат Курьеров (Courier), которые доставляют заказы и могут иметь доступ в мобильное курьерское приложение.

Меню организации наполняется администратором и подлежит публикации для формирования витрины. Публикация подразумевает, что изменение блюд выполняется сначала на уровне черновиков (drafts), которые только после выполнения публикации становятся доступными для продажи. Каждое черновое блюдо (DraftDish) содержится в черновой категории меню (DraftCategory).

Формат данных

Формат данных, который используется при взаимодействии с сервером Смартомато, частично соответствует стандарту JSON API. Этот стандарт основан на принципе REST и накладывает жесткие ограничения на формат данных при запросах для множественных или сингулярных ресурсов.

Рассмотрим пример. Предложим, что имеется ресурс Posts (доступный по URL /posts), который представляет коллекцию постов в блоге. Тогда запрос GET /posts вернет коллекцию всех постов в следующем формате:

{
  "posts": [
    { "id": 1, "text": "My first blog post" },
    { "id": 2, "text": "My second blog post" }
  ]
}

При этом запрос информации о конкретном посте GET /posts/:id вернет данные в формате (следует обратить внимание на единственное число слова post):

{
  "post": {
    "id": 1,
    "text": "My first blog post"
  }
}

Аналогичный формат используется для обновления полей конкретного поста посредством запроса PUT /posts/:id (часть полей, которые не подлежат обновлению, может отсутствовать).

Запросы на чтение могут снабжаться фильтрами, которые передаются через query parameters, а также мета-информацией. Например, для получения всех постов конкретного автора может использоваться следующий запрос GET /posts?author_id=9:

{
  "posts": [
    { "id": 3, "name": "Stand by Me",    "author_id": 9 },
    { "id": 5, "name": "From a Buick 8", "author_id": 9 }
  ],
  "meta": {
    "count": 2,
    "total": 100
  }
}

Кроме того, запросы на чтение могут содержать данные связанных ресурсов для экономии количества запросов (sideloading). К примеру, каждый пост может быть написан от лица пользователя, который представлен ресурсом Authors. Тогда запрос GET /posts/1 будет возвращать:

{
  "post": {
    "id": 1,
    "text": "My first blog post",
    "author_id": 12
  },
  "authors": [
    { "id": 12, name: "Stephen King"}
  ]
}

Стоит отметить, что не всегда действия над ресурсами, которые выполняются через API, можно вписать в схему REST. Примером может служить действие like, добавляющее пост в избранное. Такие действия обычно представляются в виде отдельного запроса вида PUT /:resource/:id/:action, то есть в данном случае PUT /posts/1/like.

Передача параметров

Параметры в методы API могут переданы: посредством query-string параметров (например, /api/method?param=value&other_param=other_value) или через тело HTTP-запроса. API Смартомато использует первый способ преимущественно для обработки фильтров при получении списков данных (пример: GET /api/orders?client_id=13) или передачи дополнительных опций (например, для отключения отправки СМС-уведомлений при смене статуса заказа).

Часть методов принимают данные в формате JSON, в этом случае обязательным является заголовок Content-Type: application/json, а данные передаются в виде сериализованной JSON-строки в тело HTTP-запроса.

Пример работы с API

В качестве быстрого старта предлагаем ознакомиться с примером скрипта на языке PHP, который демонстрирует работу с API Смартомато. Он показывает процедуру получения токена через авторизацию и создание нового чернового блюда в меню.

<?php

  /*
   * Авторизация: POST /api/session с параметрами: login, password
   */
  $auth_url = "http://smartomato.ru/api/session";

  $auth_data = array(
    "login"    => "...",
    "password" => "..." );

  $curl = curl_init($auth_url);

  curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($curl, CURLOPT_POST,           true);
  curl_setopt($curl, CURLOPT_POSTFIELDS,     $auth_data);

  echo "=> Sending POST {$auth_url}\n";

  $curl_response = curl_exec($curl);

  if ($curl_response === false) {
    $error = curl_error($curl);
    $info  = var_export( curl_getinfo($curl), true );

    curl_close($curl);
    die("An error occured during authorization: " . $error . "\nAdditional info:\n" . $info);
  }

  echo "<= {$curl_response}\n";

  /*
   * Ответ содержит информацию о пользователе и токен для доступа
   * { ... "meta": { "token": <TOKEN> } }
   */
  $json_resp = json_decode( $curl_response, true );
  $auth_token      =  $json_resp['meta']['token'];
  $organization_id =  $json_resp['staff_members'][0]['organization_id'];

  echo "[+] Obtained auth token {$auth_token} {$organization_id}\n";

  /*
   * Создадим тестовое новое блюдо через API
   */
  $api_url = "http://smartomato.ru/api/draft_dishes";

  // Сериализуем JSON в строку
  $new_dish_data = json_encode( array(
    "draft_dish" => array(
      "name"              => "Грандиозный фалафель с кунжутом",
      "price"             => 250,
      "organization_id"   => $organization_id,
      "draft_category_id" => 3
    )
  ));

  $curl = curl_init($api_url);

  curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($curl, CURLOPT_POST,           true);
  curl_setopt($curl, CURLOPT_POSTFIELDS,     $new_dish_data);

  // Выставляем необходимые заголовки
  curl_setopt($curl, CURLOPT_HTTPHEADER, array(
    // Передаем токен, который получили при авторизации
    "Authorization: Token token=\"" . $auth_token . "\"",

    // Этот заголовок необходим для передачи JSON
    "Content-Type: application/json"
  ));


  echo "=> Sending POST {$api_url}\n   {$new_dish_data}\n";

  $curl_response = curl_exec($curl);

  if ($curl_response === false) {
    $error = curl_error($curl);
    $info  = var_export( curl_getinfo($curl), true );

    curl_close($curl);
    die("An error occured during api call: " . $error . "\nAdditional info:\n" . $info);
  }

  $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
  echo "<= Status: {$httpcode}";

Авторизация

Исполнение методов API (за исключением публичных методов, работающих с корзиной заказа) в Смартомато происходит в рамках сессии. Создание сессии происходит от лица пользователя API (ApiUser). Конкретный ApiUser однозначно определяется парой логин-пароль (они же используются при инициации новой сессии) или ключом ApiKey. Кроме того, каждый ApiUser имеет привязанный аккаунт, который определяет множество действий в API, доступных данному пользователю.

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

  1. Dispatcher диспетчер Смартомато, занимающийся логистикой заказов тех ресторанов, которые не используют собственных курьеров (ему доступны фактически все заказы системы).
  2. StaffMember менеджер конкретного филиала некого ресторана. Занимается логистикой заказов, относящихся к конкретному ресторану, редактированием зон доставки и доступности меню.
  3. Courier курьер. Может видеть и принимать только те заказы, которые на него назначены.

Для вызова метода API необходимо снабдить запрос HTTP-заголовком вида Authorization: Token token="<TOKEN>", где <TOKEN> — привязанный к сессии API-ключ. Токен можно также передать посредством query-параметра token: /api/orders?token=<TOKEN>.

POST /api/session

Выполняет создание новой сессии для доступа к API через параметры login и password. Пример POST /api/session?login=user@example.com&password=password:

{
  "staff_members": [
    {
      "id": 1,
      "role": "admin",
      "organization_id": 1,
      "restaurant_ids": [ 1 ]
    }
  ],
  "api_user": {
    "id": 1,
    "login": "user@example.com",
    "name": "Steve Buschemi",
    "phone": "2-12-85-06",
    "avatar": {
      "original": "http://smartomato.ru/api_user/avatar/1/user.png",
      "small": "http://smartomato.ru/api_user/avatar/1/small_user.png",
      "medium_square": "http://smartomato.ru/api_user/avatar/1/medium_square_user.png",
      "medium": "http://smartomato.ru/api_user/avatar/1/medium_user.png"
    },
    "account": {
      "type": "staff_member",
      "id": 1
    }
  },
  "meta": {
    "token": "3505a86b5b3f0206cd29062529f88b1e"
  }
}

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

GET /api/session

Получает состояние текущей сессии.

DELETE /api/session

Выполняет выход из текущей сессии. Использованный ранее token становится неактуальным.

Ресурс Оrganization

Сущность представляет собой зарегистрированную в Смартомато организацию.

GET /api/organizations/:id

{
  "organization": {
    "id": 1,
    "name": "Setters Brew Bar",
    "host": "setters.smartomato.ru",
    "social_network": {
      "vk": "http://vk.com/settersbrewbar"
    },
    "about": "Самый лучший кофе на свете!",
    "phone": "+7 (905) 341-35-88",
    "accept_cashless": true
  }
}

Описание полей модели Organization:

  • name — название организации.
  • host — адрес витрины организации, предоставляемой Смартомато.
  • social_network — хеш, содержащий ссылки на группы организации в социальных сетях. Доступные ключи: vk, facebook, twitter, instagram.
  • about — краткое описание (слоган) организации.
  • phone — телефон организации.
  • accept_cashless — принимает ли безналичные платежи.

GET /api/organizations

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

PUT /api/organizations/:id

Обновляет данные организации. Доступные для редактирования поля: name, social_network, about, phone.

POST /api/organizations/:id/publish

Публикует меню заданной организации.

Ресурс Restaurants

Ресурс представляет рестораны, к которым авторизованный пользователь имеет доступ. В Смартомато ресторан является одни из филиалов конкретной организации и может соответствовать физическим пунктам выдачи заказов.

GET /api/restaurants/:id

Возвращает информацию о ресторане по идентификатору.

{
  "restaurants": [
    {
      "id": 1,
      "name": "Setters Brew Bar",
      "phone": "2-33-01-20",
      "address": "ул. Максима Горького, 151",
      "organization_id": 1
    }
  ]
}

GET /api/restaurants

Возвращает список доступных данному пользователю ресторанов. Возможна фильтрация ресторанов через параметр organization_id.

PUT /api/restaurants/:id

Обновляет атрибуты ресторана. Доступны для редактирования name, phone, address.

Ресурс DraftCategory

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

GET /api/draft_categories/:id

Получает информацию о заданной категории.

{
  "draft_category": {
    "id": 1,
    "name": "Альтернативный кофе",
    "menu_order": 0,
    "photo": {
        "original": "http://smartomato.dev/1.png",
        "small_square": "http://smartomato.dev/small_square_1.png",
        "medium_square": "http://smartomato.dev/medium_square_1.png",
        "large": "http://smartomato.dev/large_1.png"
    },
    "draft_dish_ids": [ 1, 12, 2, 13 ],
    "organization_id": 1
  }
}

Описание полей:

  • name — название категории.
  • menu_order — числовое поле, представляющее порядок категории на витрине Смартомато.
  • photo — хеш, содержащий разные версии фоновой фотографии категории.
  • draft_dish_ids — массив идентификаторов блюд, которые содержатся в данной категории.
  • organization_id — идентификатор организации.

GET /api/draft_categories

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

PUT /api/draft_categories/:id

Обновляет поля заданной категории.

DELETE /api/draft_categories/:id

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

PUT /api/draft_categories/:id/photo

Обновляет фотографию категории. Ожидает тип mutlipart/form-data с атрибутом file.

Ресурс DraftDish

Ресурс представляет собой черновой (еще не опубликованный) вариант блюда, которое входит в меню.

GET /api/draft_dishes/:id

Возвращает черновое блюдо по идентификатору.

{
  "draft_dish": {
    "id": 1,
    "is_hidden": false,
    "name": "Кофе из кемекса",
    "description": "Вкуснейший кофе из кемекса",
    "menu_order": 0,
    "price": "100.0",
    "photo": {
      "original": "http://smartomato.ru/test.jpg",
      "small_square": "http://smartomato.ru/small_square_test.jpg",
      "medium_square": "http://smartomato.ru/medium_square_test.jpg",
      "medium": "http://smartomato.ru/medium_test.jpg",
      "large": "http://smartomato.ru/large_test.jpg"
    },
    "weight": "50",
    "ingredients": "Кофе",
    "can_zoom": false,
    "is_next_day": false,
    "external_id": "",
    "options": null,
    "draft_category_id": 1,
    "organization_id": 1
  }

Описание полей:

  • is_hidden — скрыто ли блюдо на витрине.
  • name — название блюда.
  • description — описание блюда.
  • menu_order — целое, показывающее порядок блюда внутри категории.
  • price — цена блюда.
  • photo — хеш, содержащий разные версии фотографии блюда.
  • weight — вес блюда в граммах.
  • ingredients — строковое описание состава блюда.
  • is_next_day — показывает, что доставка блюда возможна только на следующий день.
  • external_id — строковый внешний идентификатор. Используется при интеграции виджета Смартомато на внешнем сайте.
  • options — хеш, представляющий конфигуратор блюда (возможность использования добавок во время покупки). См. раздел «Формат конфигуратора».
  • draft_category_id — идентификатор категории, к которой принадлежит блюдо.
  • organization_id — идентификатор организации, к которой принадлежит блюдо.

GET /api/draft_dishes

Возвращает список всех черновиков блюд, к которым авторизованный пользователь имеет доступ. Доступны фильтры по следующим атрибутам: organization_id, draft_category_id.

DELETE /api/draft_dishes/:id

Удаляет блюдо. Удаление блюда с витрины произойдет после публикации.

PUT /api/draft_dishes/:id

Изменяет атрибуты конкретного блюда.

POST /api/draft_dishes/

Создает новое блюдо. Поля draft_category_id, organization_id, price, name являются обязательными.

PUT /api/draft_dishes/:id/photo

Обновляет фотографию блюда. Ожидает тип mutlipart/form-data с атрибутом file.

Формат конфигуратора

Параметр options позволяет задать конфигуратор блюда, с помощью которого возможен выбор разных вариантов добавок. Выбранные во время покупки добавки формируют окончательную цену блюда в соответствии с форматом конфигуратора. Приведем пример формата конфигуратора блюда:

"options": [
  {
    "items": [
      {
        "name": "Эфиопия Фуджи",
        "price": "20"
      },
      {
        "name": "Арабика",
        "price": "30"
      },
      {
        "name": "Nescafe Gold",
        "price": 0
      }
    ],
    "max": 1,
    "min": 1,
    "multiple": false,
    "name": "Сорт кофе"
  },
  {
    "items": [
      {
        "name": "Взбитые сливки",
        "price": "10"
      },
      {
        "name": "Ореховая стружка",
        "price": "15"
      },
      {
        "name": "Корица",
        "price": "10"
      }
    ],
    "max": "3",
    "min": "0",
    "multiple": true,
    "name": "Сладкие добавки"
  }
]

Конфигуратор состоит из массива хешей, каждый из которых описывает отдельный вид добавки. Название вида добавки содержится в параметре name. Возможные варианты для выбора представлены параметром items, каждый вариант обладает названием name и добавочной ценой price.

Кроме того, существует возможность задать максимальное и минимальное количество выбираемых вариантов. Параметр multiple определяет, можно ли выбирать несколько вариантов или необходимо выбрать одно и только одну опцию. В случае, если multiple имеет значение true, параметры min и max определяют минимальное и максимальное количество опций соответственно.

Ресурс DeliveryZones

Ресурс DeliveryZones предоставляет методы для работы с зонами доставки — сущностями, которые определяют условия доставки (время доставки, цену, режим работы) в зависимости от адреса. Каждая зона принадлежит ресторану, в который в последствии поступает заказ.

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

GET /api/delivery_zones/:id

Получение информации о заданной зоне доставке.

{
  "id": 2,
  "name": "Центр Ростова",
  "delivery_time": 30,
  "price": 50,
  "timezone": "Europe/Moscow",
  "free_delivery_count": null,
  "free_delivery_price": null,
  "minimal_order_price": null,
  "area": "POLYGON ((39.692910354492184 47.22123047559831, 39.71290890490714 47.236197896650225, 39.67093769824215 47.228130049589005))",
  "restaurant_id": 1,
  "organization_id": 1,
  "timetables": [
    {
      "id": 8,
      "open_at": "2000-01-01T00:00:00Z",
      "close_at": "2000-01-01T14:00:00Z",
      "day": 0,
      "date": null,
      "is_working": true
    },
    ...
  ]
}

Описание полей:

  • name — название зоны доставки.
  • delivery_time — время доставки в минутах.
  • price — цена доставки.
  • timezone - часовой пояс зоны.
  • free_delivery_count — минимальное количество блюд, после которого доставка бесплатная.
  • free_delivery_price — цена, после которой доставка бесплатная.
  • minimal_order_price — минимальная цена заказа в этой зоне.
  • area — геометрия зоны, заданная в формате WKT. Должна быть представлена односвязной областью без самопересечений.
  • restaurant_id — ресторан, к которому принадлежит зона. Заказы в этой зоне будут попадать в этот ресторан.
  • organization_id — организация, к которой принадлежит зона.
  • timetables — расписание работы, массив объектов, которые представляют временные промежутки, в которые эта зона работает. Формат:
    • open_at — начало промежутка (учитывается только время)
    • close_at — конец промежутка (учитывается только время)
    • day — номер дня недели
    • date — специальная дата, задает время работы только в определенный день. Может отсутствовать.
    • is_working — в значении false данный промежуток не будет учитываться при построении расписания.

GET /api/delivery_zones

Запрос возвращает все зоны доставки организаций, к которым авторизованный пользователь имеет доступ. Доступна фильтрация через параметр organization_id.

POST /api/delivery_zones

Создание новой зоны достаки. Обязательными являются параметры: organization_id, restaurant_id, name.

PUT /api/delivery_zones/:id

Обновление полей заданной организации.

Ресурс Orders

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

GET /orders/:id

Получает информацию о заданном заказе по идентификатору. Формат ответа:

{
  "restaurants": [
    {
      "id": 1,
      "name": "Setters Brew Bar",
      "phone": "2-33-01-20",
      "is_running": true,
      "address": "ул. Максима Горького, 151",
      "is_delivery": false,
      "organization_id": 1
    }
  ],
  "couriers": [
    {
      "id": 3,
      "name": "Джек Николсон",
      "phone": "+7 (904) 231-23-23",
      "avatar": {
        "original": "http://smartomato.ru/av.jpg",
        "small": "http://smartomato.ru/small_av.jpg",
        "medium_square": "http://smartomato.ru/medium_square_av.jpg",
        "medium": "http://smartomato.ru/medium_av.jpg"
      },
      "organization_id": 1,
      "last_known_position": null,
      "comment": "Классно сыграл в том самом фильме.",
      "partnership_id": null
    }
  ],
  "payments": [],
  "order": {
    "id": 65,
    "number": "1-14",

    // Предполагаемое полное время доставки в минутах
    // (с момента приготовления блюда до момента получения клиентом)
    "delivery_time": 60,

    // Предполагаемое время доставки из ресторана клиенту
    "transportation_time": 25,

    // Предполагаемое время приготовления
    "cooking_time": 15,

    // Контактный телефон и имя клиента
    "contact_phone": "+7 (904) 571-25-42",
    "contact_name": "John Doe",

    // Комментарий клиента
    "description": "Привезите скорее, пожалуйста!",

    // Нужна сдача с суммы
    "change": 5000,

    // Статус доставки заказа
    "status": "complete",

    // Время создания заказа
    "created_at": "2015-02-21T21:08:39+03:00",

    // Время подтверждения курьером
    "courier_confirmed_at": "2015-07-13T16:50:57+03:00",

    // Время подтверждения рестораном
    "restaurant_confirmed_at": null,

    // Доставить как можно скорее
    "delivery_asap": true,

    // Время, к которому необходимо доставить заказ
    "delivery_at": "2015-02-21T21:38:39+03:00",

    // Координаты места назначения в формате WKT и
    // в виде широты/долготы
    "coordinates": "POINT (39.714285 47.226894)",
    "latitude": 47.226894,
    "longitude": 39.714285,

    // Город
    "city": "Россия, Ростов-на-Дону",

    // Адреc: включает улицу и дом
    "address": "улица Максима Горького, 151",

    "apartment": "18",  // Квартира/офис
    "staircase": "1й",  // Подъезд
    "floor":     "5",   // Этаж
    "doorphone": "18B", // Код домофона

    // Полный адрес, сформированный на основе вышеперечисленных полей
    // Удобен для использования в интерфейсах
    "address_text": "улица Максима Горького, 151, 1й подъезд, 5 этаж, кв. 18, код домофона 18B",

    // Предпочтительное время забора заказа из ресторана
    "pick_up_at": "2015-06-23T22:35:00+03:00",

    // Источник оплаты заказа:
    //   cash наличные
    //   card картой
    //   courier_card картой курьеру
    "payment_source": "cash",

    // Полная стоимость заказа с учетом доставки и всех надбавок
    "final_sum": 365,

    // Цена доставки
    "delivery_price": "50.0",

    // Полная стоимость всех блюд
    "total": "315.0",

    "restaurant_id": 1,

    // id клиента в организации
    "client_id": 1,

    // id назначенного на заказ курьера
    "courier_id": 3,

    // id модели Payment
    // (присутствует при безналичном типе оплаты)
    "payment_id": null,

    // URL для получения состава заказа и участников заказа
    "links": {
      "buddies": "/api/orders/65/buddies",
      "items": "/api/orders/65/items"
    },
  }
}

GET /orders

Получает список заказов, к которым пользователь имеет доступ. Доступны следующие параметры:

  • page — показать заданную страницу списка.
  • per_page — количество заказов на странице.
  • restaurant_id — показать только заказы из заданных ресторанов (может быть массивом).
  • courier_id — показать только заказы, назначенные на заданных курьеров (может быть массивом).
  • client_id - показать только заказы определенного клиента ресторана (может быть массивом).
  • status — показывать только заказы с определенным статусом.
  • sort_by — определяет поле, по которому происходит сортировка.

Данные о количестве страниц отдаются в виде мета-информации:

{
  "orders": [ ... ],
  "meta": {
    "page": 1,
    "page_count": 3,
    "per_page": 25,
    "count": 68
  }
}

GET /orders/:id/receipt{.pdf|.html}

Возвращает печатную выписку заказа в заданном формате (HTML или PDF).

PUT /orders/:id

Запрос необходим для редактирования следующих полей заказа: courier_id, status, contact_name, contact_phone, description, delivery_at, delivery_price.

PUT /orders/:id/confirm

Запрос необходим для подтверждения заказа курьером или рестораном. Тип подтверждения определяется на основе текущей учетной записи. В результате операции будут обновлены поля restaurant_confirmed_at или courier_confirmed_at, которые показывают время подтверждения рестораном или курьером соответственно.

PUT /orders/:id/status?status=VALUE

Метод доступен курьеру и необходим для выставления текущего статуса заказа. Доступные значения параметра status для курьера:

  • delivery заказ получен и доставляется
  • complete заказ доставлен

Методы импорта

API Смартомато позволяет выполнять пакетный импорт данных, таких как меню, заказы, из внешних систем. Ключевым параметром при импорте является внешний идентификатор (external_id), то есть идентификатор сущности в рамках внешней системы, это может быть, например, идентификатор блюда в CMS, на которой построен сайт, подключенный к Смартомато посредством виджета.

Импорт меню

Для ресторанов с уже готовым сайтом существует возможность быстрого импорта меню. Для этого может быть использован метод API POST /api/organizations/:id/import/menu. Импорт подразумевает полную замену меню ресторана и поддерживает разные форматы описания связи блюд с категориями.

Замечание #1: импорт меню работает с полной заменой текущего меню организации. Это означает, что для дополнения меню новыми блюдами необходимо передавать уже существующие блюда (через external_id) наряду с новыми.

Замечание #2: импорт меню выполняется асинхронно. Это значит, что ответ от сервера может быть получен до момента, когда импорт завершится. Одновременно может происходить лишь один импорт меню в организации, поэтому новый импорт не будет выполнен, если в данный момент еще не завершена предыдущая операция. Ответ от сервера в этом случае содержит информацию о текущей задаче и ходе ее выполнения:

{
  "status":      "busy",
  "job_id":      "5c1e1e79a0582b969cd9d4d0",
  "job_status":  "working",
  "job_message": "Importing dish Muscovado Crunch",
  "job_percent": 8.51063829787234
}

Ответ в случае успешного начала импорта:

{
  "status": "queued",
  "job_id": "5c1e1e79a0582b969cd9d4d0"
}

Запросить статус текущего импорта в любой момент времени можно с помощью метода GET /api/organizations/:id/import/menu. Рекомендуется использовать именно этот метод для мониторинга состояния импорта после его начала.

Входные данные для метода импорта (используйте заголовок Content-Type: application/json):

{
  // Список категорий может отсутствовать. В таком случае категории для блюд
  // создаются автоматически. Для привязки блюд одним и тем же категориям
  // можно также использовать `category_name` блюда.
  "categories": [
    {
      // Название категории
      "name": "Пончики",

      // Порядок в меню (может отсутствовать)
      "menu_order": 0,

      // Ссылка на фотографию категории
      "remote_photo_url": "http://goo.gl/IqZ4hE",

      // Строковый внешний идентификатор для связки блюда и категории
      "external_id": "ponchiki"
    }
  ],
  "dishes": [
    {
      "name": "Ультра-Шоколад",
      "price": 140.50,
      "weight": 200.00,
      "menu_order": 0,
      "description": "Необычайно вкусный пончик с двойной шоколадной начинкой",

      // Список конфигураторов, может быть null
      "modifiers": [
        {
          "name": "Экстра-Добавки",
          "items": [
            { "name": "Марцепан", "price": 15 },
            { "name": "Орехи",    "price": 25 }
          ],
          "min": 1,
          "max": 2,
          "multiple": false
        }
      ],

      // Карта доступности блюда в ресторанах, опционально
      // Доступные значения поля "status"
      //   "available" - по-умолчанию, блюдо доступно в ресторане
      //   "not_available" - блюдо недоступно в ресторане
      //   "call" - заказ можно совершить, но наличие блюда стоит уточнить
      //
      // Если массив availability указан, блюдо получает статус "available"
      // во всех неперечисленных ресторанах
      "availability": [
        { "restaurant_id": 1,  "status": "call" },
        { "restaurant_id": 12, "status": "not_available"}
      ],

      // Ссылка на фотографию блюда
      "remote_photo_url": "http://goo.gl/IqZ4hE",

      // Привязка к категории может осуществляться двумя способами:
      // 1. Через внешний идентификатор категории (категория должна быть
      //   представлена в массиве categories)
      "category_external_id": "ponchiki",

      // 2. Через имя категории (в случае отсутствия категории в categories,
      // она создается автоматически)
      "category_name": "Пончики",

      // Опциональный внешний идентификатор
      "external_id": "ultra-choko"
    }
  ],
  // Опциональный конфигуратор импорта
  "config": {
    // Позволяет полностью заменить текущее меню импортируемым. По-умолчанию: false
    "destroy_menu": true,

    // Публикация меню сразу после завершения импорта. По-умолчанию: false
    "publish_menu": true
  }
}

Внешние идентификаторы external_id блюд и категорий могут быть использованы для:

  • Интеграции виджета с сайтом ресторана (см. Размещение виджета на стороннем сайте).
  • Связи импортируемых категорий и блюд (через указание category_external_id).
  • Поддержки итеративного импорта: обновленное меню можно импортировать без потери статистики в Смартомато. Наличие при импорте ранее импортированного блюда external_id гарантирует, что блюдо будет сохранять идентификатор в системе Смартомато.

Импорт клентов

Импорт клиентов производится аналогично импорту номенклатуры меню. Процесс также является асинхронным, поэтому для импорта доступны два метода GET /api/organizations/:id/import/clients и POST /api/organizations/:id/import/clients c идентичным форматом для описания текущей задачи в очереди.

Формат данных для импорта клиентов:

{
  "clients": [
    {
      // Внешний идентификатор
      "external_id": "fa860",

      "email": "bulkin@smartomato.ru",

      // male или female
      "gender": "male",

      // Дата рождения в формате дд/мм/гггг
      "birthdate": "15/01/1978",

      // Дата регистрации
      "registered_at": "2009-02-09T21:00:00.000Z",
      "name": "Виталий Булкин",

      // Комментарий к клиенту (не виден клиенту)
      "comment": "будьте осторожны с этим парнем, не платит за заказ!",

      "addresses": [
        "ул. Пресненский вал д.8 под.2 эт.2 кв.41"
      ],

      "phone_numbers": [
        "+7 (926) 152-65-21"
      ],

      // Список тегов
      "tags": [
        "не платит",
        "новый гость"
      ],

      // В черном списке?
      "is_blacklisted": true
    }
  ]
}

Иницирирование импорта может сопровождаться переданным query-параметром from_scratch=true, в таком случае ранее созданные клиенты будут удалены перед импортом.

Пакетный импорт и создание заказов

Загрузка существующей истории заказов, а также создание новых заказов возможно при помощи метода POST /api/organizations/:id/import/orders.

Query-параметр existing позволяет задать стратегию для обновления уже импортированных заказов (посредством external_id). Доступны следующие режимы:

  • update обновить заказ, если он уже был импортирован
  • ignore пропустить заказ в случае повторного импорта. Данный режим используется по-умолчанию, если параметр existing не задан.

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

На вход метода поступает массив заказов в следующем формате:

{
  "orders": [
    {
      "external_id": "d35592e1-c85d-4ec3-b069-cfcfdf3cf1ba",
      "city": "Москва",
      "address": "Чертаново Северное микрорайон 6",
      "comment": "сдача с 1000",
      "change": 1000,

      // Время размещения заказа
      "published_at": "...",
      "delivery_at":  "...",

      "total": 1000,

      // Сумма, оплаченная бонусами
      "used_bonuses": 100,

      // (!) Импорт заказов в статусе "новый" приведет
      // к отсылке СМС и e-mail уведомлений о новом заказе
      "status": "pending",

      // Адрес обязательно необходимо указать в координатах,
      // иначе заказ не сможет быть привязан к нужному ресторане
      // и зоне
      "latitude":  55.634126,
      "longitude": 37.590515,

      // В противном случае, ресторан и зону можно
      // указать вручную
      "restaurant_id": 12,
      "delivery_zone_id": 102,

      // Варианты для передачи клиента:
      // через client_external_id
      //   => будет присвоен клиент, который найдется по
      //      external_id, если не найден, он создается
      //      в качестве имени используется contact_name
      // через client_id
      //   => будет присвоен конкретный клиент Смартомато,
      //      если он найден, если не найден, ошибка
      "client_external_id": "d35592e1-c85d-4ec3-b069-cfcfdf3cf1ba",

      "contact_phone": "...",
      "contact_name":  "...",

      "payment_source": "cash",

      // Источник заказа
      "source": "board",
      "delivery_asap": true,
      "takeaway": false,

      "buddies": [
        {
          "id": "16521630-6ea1-4756-a4b7-21e118754a3b",
          "name": "Максим",

          "items": [
            {
              // Блюдо может быть передано через:
              //  dish_external_id и dish_id
              //  (см. импорт меню)
              "amount": 2,
              "config": null,
              "name": "Котлета мясная в томатном соусе ",
              "price": 210,

              // Цена одного элемента
              "item_price": 105,
              "comment": "Больше соуса"
            }
          ]
        }
      ]
    }
  ]
}

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

{
  "organization_import": [
    {
      "external_id": "2345de2a-bcfc-483e-bfc4-27fb3d9cb11a",
      "status": "imported",
      "imported_at": "2015-10-28T17:35:14.472Z"
    },

    {
      "external_id": "6f2a60e9-08f0-4bcc-aa1c-049dbb2f14c0",
      "status": "ignored"
    },

    {
      "external_id": "a311efcb-2826-4f93-9e18-ea1a9734c607",
      "status": "error",
      "errors": [ ... ]
    }
  ]
}