ELPOS

Для разработчиков · API v1 · контракт 1.2

Подключите вашу CRM
и бухгалтерию к ELPOS

Единый интерфейс к контрагентам, товарам, складу и документам Odoo. Штатные права доступа и защита от дублей при повторных запросах.

Открыть Swagger →Скачать OpenAPI JSON

Это документация опубликованного контракта. Доступ к API на вашем сервере требует отдельного подключения. Публикация этой страницы не означает установку API в demo. Здесь не нужно вводить ключи доступа.

Что доступно сейчас

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

РесурсЧтениеСоздание
contactsКонтрагентыНовый контрагент
productsТовары, цены, единицы измерения—
stockОстатки и резерв по внутренним местам хранения—
leadsСделки CRMНовая сделка
sale-ordersЗаказы продажКоммерческое предложение
purchase-ordersЗаказы закупокЗапрос предложения поставщику
invoicesСчета и возвратные документыЧерновик счёта покупателю или поставщику
paymentsБухгалтерские платежи—
pos-ordersПродажи кассы, строки и оплаты POS—

Отдельный маршрут POST /sales позволяет интегратору выбрать кассовую продажу или подтверждённый заказ со счётом и оплатой. Требуется обновлённое подключение API с правами sales:read,sales:write. Регистрация оплаты не означает подтверждение списания банком или готовность фискального чека.

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

Как подключиться

  1. Уточните у администратора адрес сервера ELPOS, на котором включён API. Базовый путь: /api/elpos/v1. Для внешнего доступа используйте HTTPS.
  2. Администратор создаёт отдельного внутреннего пользователя с нужными ролями Odoo, затем подключение в Настройки → External business API: компания, пользователь и права по ресурсам. Пользователь подключения не должен быть администратором настроек.
  3. Получите отдельный ключ для вашей системы и сохраните его в защищённых настройках вашего сервера. Ключ показывается один раз; в ELPOS хранится его хеш.
  4. На своём сервере откройте /api/elpos/v1/docs, нажмите Authorize и проверьте GET /ping. На публичном Swagger доступны только просмотр схем и примеров.
  5. Проверьте обмен на тестовой базе, затем выдайте рабочему подключению минимально необходимые права.

Права задаются отдельно: например, contacts:read, sale-orders:write. Они не расширяют штатные права пользователя. Каждое подключение ограничено одной компанией; общие товары и контакты доступны только согласно штатным правилам.

Архивация подключения или смена ключа прекращает прежний доступ. Ключ не размещают в браузерном коде, URL или мобильной сборке. Запросы выполняет сервер внешней системы; CORS не поддерживается.

Реальная продажа: выбор интегратора

В POST /api/elpos/v1/sales передайте mode: "pos" либо mode: "sale". Это отдельный маршрут: прежний POST /sale-orders по-прежнему создаёт коммерческое предложение.

РежимЧто создаётсяЧто передать
posОплаченный кассовый заказ и оплаты POS в указанной открытой смене.config_id, session_id, товары и оплаты с payment_method_id.
saleПодтверждённый заказ продажи, проведённый счёт покупателю и штатные платежи, сверенные со счётом.partner_id, товары и оплаты с journal_id, payment_method_line_id.

Получить способы оплаты

GET /sales-options?mode=pos показывает кассы, открытые смены и способы оплаты. GET /sales-options?mode=sale — журналы кассы/банка и входящие способы оплаты. Используйте ID из ответа: одного названия «карта» недостаточно. Поддерживаются limit и after_id. Для POS выбирайте кассу с supported: true. Если payment_methods_truncated: true, список способов оплаты неполон — уточните настройку у администратора.

Пример для кассы

Пример рассчитан на итог 200 в валюте кассы. Все ID и суммы замените данными вашей базы; итог с налогами проверяет Odoo.

{
  "mode": "pos",
  "config_id": 10,
  "session_id": 12,
  "reference": "CRM-1007",
  "lines": [{"product_id": 20, "quantity": 2, "price_unit": 100}],
  "payments": [{"payment_method_id": 5, "amount": 200}],
  "expected_total": 200
}

Пример для раздела «Продажи»

{
  "mode": "sale",
  "partner_id": 42,
  "reference": "CRM-1008",
  "lines": [{"product_id": 20, "quantity": 2, "price_unit": 100}],
  "payments": [{"journal_id": 4, "payment_method_line_id": 6, "amount": 200}],
  "expected_total": 200
}

Для каждого запроса нужны Bearer-ключ и свой Idempotency-Key. Передавайте прежний ключ и тело при повторе после таймаута. Можно разделить оплату на несколько ручных способов; сумма должна точно совпадать с итогом.

Что означает успешный ответ

record — неизменяемый результат создания. current — актуальные состояния заказа, счёта, платежей, задолженности и складских документов. Повтор POST возвращает тот же record и свежий current. Также доступен GET /sales/{mode}/{id} для продаж этого подключения.

API регистрирует заявленную ручную оплату: provider_verified: false. Он не списывает деньги через банк или эквайера. У счёта может быть нулевая задолженность и состояние in_payment до банковской сверки. Проверяйте валюту каждой оплаты и флаги усечения в ответе.

В режиме sale складская отгрузка проходит обычным процессом Odoo и автоматически не подтверждается. В POS расход зависит от настройки кассы — сразу или при закрытии смены. Оплаченный заказ не доказывает отгрузку.

Если в кассе подключена поддерживаемая фискализация, действует штатная очередь. Смотрите current.fiscal: ожидание или ошибка не означают готовый фискальный чек. В режиме sale кассовая фискализация этим маршрутом не выполняется.

Границы маршрута /sales

Доставка с долгом: ELCRM → ElJet → ElPOS

Контракт 1.2 проверен на изолированном стенде. Документация не означает установку модуля на рабочие серверы. Автоматический выпуск фискальных чеков доставки в адаптере пока не реализован; импорт внешних реквизитов описан отдельно.

Для маршрутной доставки используется раздел «Продажи»: принятый товар отражается в складе и счёте покупателю независимо от оплаты. Новые методы находятся под /api/elpos/v1/delivery. Не вызывайте дополнительно /sales для той же доставки.

ОперацияМетод после /deliveryРезультат
План заказаPOST /ordersПодтверждённый заказ с исходными строками и ценами.
Рейс и погрузкаPOST /trips
POST /trips/{external_id}/loads
Фактическая передача склад → водитель.
ДоставкаPOST /delivery-resultsТолько принятый товар: водитель → клиент и проведённый счёт. Оплата необязательна.
ДоплатаPOST /paymentsОплата существующего счёта без нового заказа и расхода товара.
Непринятые остаткиPOST /trips/{external_id}/stock-returnsФактический возврат водитель → склад, без кредит-ноты.
Закрытие рейсаPOST /trips/{external_id}/closeТолько при нулевых остатках; автоматического обнуления нет.
Возврат после продажиPOST /returnsОбратное движение и кредит-нота исходного счёта.
Выплата клиентуPOST /refundsОтдельная регистрация возврата денег.
Внешний чекPOST /fiscal-receiptsРеквизиты EKASSA от ELCRM без повторного вызова ККМ.
Восстановление связиGET /lookupПо внешнему ID и виду, event_id либо idempotency_key.

До первого проведения настройте счета, журналы и ручные способы оплаты. Для компании Кыргызстана заполните профиль учёта: правовую форму, налоговый режим и статус НДС; одного выбора страны и валюты недостаточно.

Пример задолженности

Доставлено на 1 000 сом без оплаты → долг 1 000. Затем 400 наличными → долг 600. Подтверждённые ELCRM 600 через банк → долг 0. ElPOS регистрирует оплату, не списывая деньги у эквайера.

Загружено 10 единиц, клиент принял 8: расход клиенту равен 8, у водителя остаются 2. Возврат двух непринятых единиц на склад не создаёт новую продажу. Возврат уже принятого товара сначала уменьшает долг или создаёт кредит клиента; выплата оформляется отдельно.

Повторы и состояния

Каждый POST получает Idempotency-Key, event_id и external_id. Сохраняйте тело до отправки. После таймаута ищите операцию и повторяйте прежний запрос. Идентичность доставки сохраняется при смене ключа доступа. Изменённое содержание под прежним ID даёт конфликт.

current.settlement отделяет выставленную сумму, кредит-ноты, реальные оплаты, возвраты денег, долг и кредит клиента. payment_status, refund_status, складские документы и current.fiscal возвращаются отдельно. Сумма исходного заказа не подменяет сумму частичной доставки.

item_id ElJet — UUID строки заказа. Для товара нужно проверенное сопоставление с вариантом product.product, а не шаблоном. Проведённая история старых заказов автоматически не мигрирует.

Семь сценариев доставки и оплаты: примеры JSON

Фискальная граница и ограничения

Владелец фискализации — ELCRM. Переданный чек получает reported_by_elcrm и independently_verified: false: это сохранённые внешние реквизиты, а не самостоятельная проверка EKASSA. Повторная фискальная очередь ElPOS не создаётся. Накладная Xprinter — нефискальный документ.

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

Выгрузка для бухгалтерии — дополнение 1.3

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

Клиенты есть и в POS: /contacts/{id}/pos-orders показывает кассовые покупки выбранного клиента, включая возвраты. Связанный бухгалтерский документ может быть индивидуальным счётом или общей проводкой смены. Не складывайте оборот POS и счета повторно; задолженность рассчитывается по проведённым проводкам. Продажи без выбранного клиента не включаются в его историю.

/contacts/{id}/ledger и /customer-balances отделяют долг от кредита клиента. Текущий остаток не означает восстановленный долг на прошлую дату. /trial-balance показывает начальное сальдо и обороты в валюте компании по проведённым записям без условного закрытия года.

Оценка stock.move и корректировки product.value отражают штатную модель Odoo20. Если ELPOS сохранил снимок закрытия смены, API выдаёт ожидаемую сумму, пересчитанную наличность и расхождение. Для старых смен без снимка неизвестное расхождение передаётся явно, без подмены нулём.

Фильтрация по клиенту использует точный partner_id. Установка версии 1.3 на сервер конкретной компании проверяется отдельно; опубликованная документация не подтверждает развёртывание.

Пример создания черновика

POST /api/elpos/v1/sale-orders создаёт коммерческое предложение. Замените ID контрагента и товара значениями из вашей базы.

Authorization: Bearer <ключ>
Content-Type: application/json
Idempotency-Key: crm-order-1007

{
  "partner_id": 42,
  "client_order_ref": "CRM-1007",
  "lines": [{"product_id": 65, "quantity": 2}]
}

Без переопределения цены и налоги рассчитывает Odoo. Первый успешный запрос возвращает 201, повтор того же запроса — 200 с replayed: true.

Если связь оборвалась

До отправки сохраните тело и уникальный Idempotency-Key. После таймаута повторите тот же ключ и то же тело. Новый ключ может создать новый документ. Изменённое тело или другой маршрут с прежним ключом возвращают 409.

Повтор возвращает сохранённый результат создания после проверки текущих прав. Чтобы узнать сегодняшнее состояние документа, выполните GET по его ID.

Синхронизация и ограничения

Ошибки

401 — ключ недействителен; 403 — недостаточно прав; 404 — запись не найдена или недоступна; 409 — конфликт ключа повтора; 422 — неверные данные; 500 — внутренняя ошибка. Формат: {"error":{"code":"…","message":"…"}}.

Контракт и проверка

Спецификация OpenAPI 3.0.3 содержит поля, обязательные параметры, ответы и примеры. Интерфейс Swagger загружается с этого сайта, без внешнего CDN и валидатора.

Пройдены изолированные проверки HTTP, прав, компаний, конкурентных повторов и соответствия ответов OpenAPI. Подключение конкретной внешней системы проверяется отдельно.