26 мая 2026 · 1С, ODATA, HTTP · 13 мин

Разбор по 1С ODATA endpoints

Содержание

  1. Сквозные query-параметры
  2. Сквозные HTTP-заголовки
  3. Сквозные коды ошибок
  4. Метаданные и discovery
  5. Чтение коллекций
  6. Чтение одиночных сущностей
  7. Создание объекта (POST)
  8. Обновление объекта (PUT/PATCH)
  9. Удаление объекта (DELETE)
  10. Действия документов: Post / Unpost
  11. Виртуальные таблицы регистров (функции)
  12. План обмена: SelectChanges / NotifyChangesReceived

1. Сквозные query-параметры

Применяются на нескольких endpoint'ах. В таблице — где разрешены.

ПараметрГде работаетЧто делаетПодводные камни
$format=json | atomGET почти вездеФормат тела ответа. По умолчанию atom-xml. Альтернатива — заголовок Accept.На $metadata и SelectChanges игнорируется — всегда XML.
$format=application/json;odata=minimalmetadata | nometadata | fullmetadataGET сущности/коллекции в jsonУровень метаданных в json-ответе.nometadata режет @odata.type — это сломает последующий PATCH табличных частей и наборов записей. По умолчанию minimalmetadata.
$select=<list> | * | **GET сущности и коллекцииСписок запрашиваемых свойств. * — все. ** — все, кроме табличных частей.____Presentation (четыре подчёркивания) не попадает в * и ** — указывать явно.
$expand=<nav>GET коллекции, GET виртуальной таблицыРазворачивает связанные сущности inline. Запятая — разделитель.Не работает на одиночной сущности, на табличных частях, на составных реквизитах без явного указания типа (Договор/* — нельзя, Договор/Document_X/* — можно). Развёрнутые составные кладутся в свойство с суффиксом _Expanded.
$filter=<expr>GET коллекции, аргумент функций виртуальных таблиц (Condition=...)Условие отбора. См. odata.md §17.4.5.2.Не поддерживаются length, indexof, replace, tolower, toupper, trim, years, days, hours, seconds, floor, ceiling. Сравнение составного реквизита со ссылкой работает только через cast()Реквизит eq guid'...' молча вернёт пусто.
$orderby=<field> [asc|desc], ...GET коллекцииУпорядочивание. Допускается навигация Контрагент/ИНН.Тот же набор функций, что в $filter.
$top=<N>GET коллекцииОграничение количества записей.Гасится $inlinecount=allpages.
$skip=<N>GET коллекцииПропуск первых N записей. Применяется до $top.Гасится $inlinecount=allpages.
$inlinecount=allpages | noneGET коллекцииВключает в ответ общее число записей (m:count в atom, odata.count в json).Игнорирует $top и $skip — листать и считать одновременно нельзя. Считать отдельным запросом через /$count.
$count (как часть пути, не query)GET .../EntitySet/$countВозвращает только число (text/plain).См. §5.
allowedOnly=trueGET коллекции толькоВозвращает только записи, разрешённые ограничением доступа к данным. Без него запрос упадёт в 401 при попадании запрещённых строк в выборку.Не работает на POST/PATCH/DELETE и на одиночных сущностях.

Литералы в URL/значениях параметров

  • Edm.Guidguid'8d04ce3c-6360-11e9-dd92-fa163e8ba11d'
  • Edm.DateTimedatetime'1945-05-09T06:00:00'
  • Одинарные кавычки внутри строк экранируются удвоением: 'O''Brien'
  • Кириллица в именах сущностей и реквизитов должна быть percent-encoded в URL (httpx делает это автоматически).

2. Сквозные HTTP-заголовки

ЗаголовокЗапросыНазначение
Accept: application/jsonGETАльтернатива $format=json. На $metadata и SelectChanges игнорируется.
Content-Type: application/json | application/atom+xmlPOST / PUT / PATCHФормат тела запроса.
If-Match: <DataVersion>PATCH / DELETEОптимистическая блокировка. Значение — DataVersion, как пришло от 1С, opaque-строка (хранить сырьё, не пересоздавать). При несовпадении — 412 Precondition Failed, надо перечитать и повторить.
1C_OData-DataLoadMode: truePOST / PUT / PATCHЭмулирует ОбменДанными.Загрузка=Истина — отключает проверки и автозаполнения. Использовать только при миграции/синхронизации.

В ответ на успешное создание POST сервер возвращает заголовок Location с каноническим URL созданной сущности.


3. Сквозные коды ошибок 1С

При HTTP 4XX тело — XML с <m:code> и <m:message>:

<m:error xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
    <m:code>9</m:code>
    <m:message>Экземпляр сущности "НакладнаяОтгрузки" не найден по переданному ключу.</m:message>
</m:error>
КодОписаниеТипичный контекст
0Возможность не поддерживаетсялюбые запросы
1Не удалось разобрать строкупарсинг URL/тела
2Неверный формат запросалюбой
3Запрошенный тип представления не поддерживается$format на $metadata/SelectChanges
4Неверное значение свойстваPOST/PUT/PATCH
5Отсутствует обязательное значение свойстваPOST/PUT
6Неверный URLлюбой
7Не хватает элемента ключа сущностиGET/PATCH/DELETE одиночной сущности с составным ключом
8Тип сущности не найденнеизвестный Catalog_X/Document_X
9Экземпляр сущности не найденGET/PATCH/DELETE по guid
10Запрошенное свойство не найдено$select, dot-traversal
11Метод не найденвызов несуществующей функции/действия
12Отсутствует обязательный аргумент методавызов функции виртуальной таблицы
13Создание строк табличных частей напрямую не поддерживаетсяPOST на Document_X_Товары
14Ошибка разбора опций запроса$filter, $expand, $orderby
15Сущность с таким ключом уже существуетPOST
16Не удалось присвоить свойствоPOST/PUT/PATCH
17Объект не поддерживает режим загрузки данных1C_OData-DataLoadMode: true
18В объекте есть свойства с одинаковыми именамиинициализация интерфейса
19HTTP-метод запрещён в данном контекстеPUT на коллекцию, POST на сущность и т. п.
20Нет прав на действие, либо выборка попала под ограничение без allowedOnlyлюбой
21Вызов нереализованной функции / неверные аргументы / нереализованная лямбдафункции/действия/$filter

HTTP 5XX — ошибка на сервере, тело может быть произвольным.

Отдельные стандартные HTTP-коды:

  • 401 — попадание под ограничение доступа к данным без allowedOnly=true (см. код 20).
  • 404 — ресурс/сущность не найдены (часто сопровождается кодом 9 или 8).
  • 412If-Match не совпал с текущим DataVersion.
  • 501 — попытка прочитать тип, который OData не поддерживает (см. odata.md §17.4.3).

4. Метаданные и discovery

4.1. GET B/ — упрощённое описание (список сущностей)

ПараметрЗначение
МетодGET
URLB/
Path / query
HeadersAccept: application/xml (по умолчанию).
Body
ResultСписок доступных сущностей, XML-документ (atom service document).
Errors4XX, 5XX без специфики.

4.2. GET B/$metadata — полное описание (CSDL)

ПараметрЗначение
МетодGET
URLB/$metadata
Body
ResultXML CSDL: все сущности, атрибуты, навигационные свойства, функции.
ErrorsЗапрос ?$format=json или Accept: application/jsonкод 3 (в реальности 1С молча отдаёт XML, но семантически — «формат не поддерживается»).
Подводный каменьJSON-вариант метаданных не существует. Управлять детализацией json-ответов сущностей (не метаданных) можно через $format=application/json;odata=<level>.

5. Чтение коллекций

Все варианты ниже — GET на endpoint набора сущностей. Возвращают atom-feed или json-объект с массивом value.

5.1. GET B/<Prefix>_<Имя> — набор сущностей

ПараметрЗначение
Path<Prefix> ∈ {Catalog, Document, DocumentJournal, Constant, ExchangePlan, ChartOfAccounts, ChartOfCalculationTypes, ChartOfCharacteristicTypes, InformationRegister, AccumulationRegister, CalculationRegister, AccountingRegister, BusinessProcess, Task}.
Query$filter, $select, $expand, $orderby, $top, $skip, $inlinecount, $format, allowedOnly. См. §1.
HeadersAccept.
Result (json){"odata.metadata": "...", "value": [{...}, ...]} (+ odata.count при $inlinecount=allpages).
Result (atom)<feed> с <entry> (+ <m:count> при $inlinecount).
Errors8 (тип не найден), 14 ($filter/$orderby), 20/401 (без allowedOnly и ограничение доступа), 21 (нереализованные функции).

5.2. GET B/<Prefix>_<Имя>_<ТабличнаяЧасть> — все строки табличных частей всех объектов

ПараметрЗначение
URLB/Document_РасходТовара_Товары
Queryте же что в 5.1; но $expand тут не поддерживается (расширение реквизитов табличных частей не работает).
ResultКоллекция строк табличных частей (читаемая).
Errors14, 20/401.

5.3. GET B/<Prefix>_<Имя>/$count — только количество

ПараметрЗначение
URLB/Catalog_Товары/$count?$filter=Цена gt 500
Query$filter (другие игнорируются).
Resulttext/plain с числом, например 19.
Errors14 ($filter).
ЗачемСчитать вместе с листанием — $inlinecount=allpages гасит $top/$skip. Через /$count можно получить число и параллельно листать обычным GET с пагинацией.

5.4. GET B/<Prefix>_<Имя>?$inlinecount=allpages — данные + количество

| Особенность | Возвращает и записи, и odata.count / <m:count>. Игнорирует $top и $skip. |


6. Чтение одиночных сущностей

6.1. GET B/<Prefix>_<Имя>(<key>) — канонический URL экземпляра

ПараметрЗначение
PathПростой ключ: Catalog_Товары(guid'value'). Составной ключ — пары через запятую: InformationRegister_КурсыВалют(Period=datetime'2008-02-05T00:00:00', Валюта_Key=guid'9d5c4222-...'). Если ключ один — имя можно опустить (guid'...' без Ref_Key=).
Query$select, $format. $expand тут не работает.
ResultОдин объект сущности с полями + DataVersion (для последующего If-Match).
Errors7 (неполный составной ключ), 8, 9 (не найден → 404), 10 ($select несуществующего поля), 20/401.

6.2. GET B/<Prefix>_<Имя>_RowType(Ref_Key=guid'...', LineNumber=N) — строка табличной части

ПараметрЗначение
URLB/Document_РасходТовара_Товары(Ref_Key=guid'value', LineNumber=1)
КлючКомпозитный, оба компонента обязательны.
ResultОдна строка табличной части (только чтение).
Errors7, 9.
NoteПрямого PATCH/DELETE на строку нет — табличные части пишутся целиком через PATCH родителя (§8.3).

6.3. GET B/InformationRegister_<Имя>(<Recorder_Key>=guid'...') — набор записей подчинённого регистра

ПараметрЗначение
URLB/InformationRegister_ПриходныеЦены(Recorder_Key=guid'value')
ResultНабор записей, привязанных к регистратору. RecordSet — массив.
Errors7, 9.

6.4. GET B/InformationRegister_<Имя>_RecordType(<dims>) — одна запись регистра

ПараметрЗначение
URLB/InformationRegister_ПриходныеЦены_RecordType(Товар_Key=guid'value', ТипЦены='Приходная')
КлючВсе измерения регистра + (для подчинённых) Recorder_Key.
ResultОдна запись регистра, только чтение.
Errors7, 9.

6.5. GET B/AccountingRegister_<Имя>(<Recorder_Key>) — набор записей регистра бухгалтерии по регистратору

ПараметрЗначение
URLB/AccountingRegister_РегистрБухгалтерииCКорреспонденцией(guid'value')?$format=json
Resultjson-документ с Recorder_Key и массивом RecordSet.
NoteИспользуется как первый шаг для PATCH набора записей регистра бухгалтерии (§8.4).

6.6. Dot-traversal: GET B/<Prefix>_<Имя>(<key>)/<Реквизит>/<Свойство>

ПараметрЗначение
URLB/Document_РасходТовара(guid'value')/Валюта/Description
НазначениеПолучить значение свойства связанной сущности по цепочке через /.
NoteДля стандартных реквизитов в цепочке использовать только английские имена (Description, Code, ...).
Errors9, 10, 7.

7. Создание объекта (POST)

7.1. POST B/<Prefix>_<Имя> — создать сущность

ПараметрЗначение
URLURL набора сущностей: B/Catalog_Товары
HeadersContent-Type: application/json или application/atom+xml; (опц.) 1C_OData-DataLoadMode: true.
Body (json)Объект со значениями полей. Ссылочные поля — через <Реквизит>_Key. Лишние поля молча игнорируются.
Body (atom)<entry> с <m:properties>. Категория задаётся через <category term="StandardODATA.Catalog_<Имя>" .../>.
Result201 Created; Location: B/<Prefix>_<Имя>(guid'<новый-Ref_Key>'); тело — сущность с проставленными Ref_Key, DataVersion.
Errors4 (неверное значение), 5 (нет обязательного поля), 13 (попытка POST на _<Товары> — табличные части не создаются напрямую), 15 (ключ уже занят), 16, 17 (объект не поддерживает DataLoadMode), 19 (POST на сущность вместо коллекции), 20.

7.2. Замечания по ссылочным полям

  • В POST ссылки через <Реквизит>_Key: "Поставщик_Key": "086715b0-...".
  • В PUT — через @odata.bind (см. §8.2).
  • В составных полях — пара <Реквизит> + <Реквизит>_Type (например "StandardODATA.Catalog_Контрагенты"); если Undefined — значение игнорируется.

8. Обновление объекта (PUT/PATCH)

8.1. PATCH B/<Prefix>_<Имя>(<key>) — частичное обновление

ПараметрЗначение
URLB/Catalog_Магазины(guid'value')?$format=json
HeadersContent-Type; (опц.) If-Match: <DataVersion>; (опц.) 1C_OData-DataLoadMode: true.
BodyТолько изменяемые поля. Пропущенные поля сохраняются.
ResultОбновлённая сущность (либо 204 No Content, в зависимости от конфигурации).
Errors4, 7, 9, 16, 17, 19, 20; 412 при несовпадении If-Match.

8.2. PUT B/<Prefix>_<Имя>(<key>) — полное обновление

ПараметрЗначение
BodyВсе свойства сущности.
Ссылочные реквизитыТолько через <Реквизит>@odata.bind: "Организация@odata.bind": "Catalog_Организации(guid'...')". Можно полный URI или короткий путь после standard.odata.
ErrorsТе же, что у PATCH; плюс 5 (если что-то обязательное забыли).
Note_Key плоской записью в PUT не используется — связи только через @odata.bind.

8.3. Табличные части — целиком и только через PATCH родителя

ПараметрЗначение
URLPATCH B/Catalog_Магазины(guid'value')?$format=json
BodyПомимо обычных полей: "<ТЧ>@odata.type": "Collection(StandardODATA.<Prefix>_<Имя>_<ТЧ>_RowType)" + "<ТЧ>": [ {LineNumber, ...}, ... ].
CriticalПередавать весь массив строк, даже если меняется одна. Частичный апдейт строки невозможен.
Errors13 (попытка прямого POST на _<ТЧ>), 4, 16.

8.4. Набор записей регистра бухгалтерии — read → modify → PATCH

Последовательность:

  1. GET B/AccountingRegister_<Имя>(guid'<Recorder>')?$format=json — прочитать текущий набор.
  2. Модифицировать массив RecordSet локально (изменение/добавление/удаление строк).
  3. PATCH B/AccountingRegister_<Имя>(guid'<Recorder>') с телом:
    {
      "odata.type": "StandardODATA.AccountingRegister_<Имя>_RowType",
      "Recorder_Key": "<Recorder>",
      "RecordSet@odata.type": "Collection(StandardODATA.AccountingRegister_<Имя>_RowType)",
      "RecordSet": [ /* строки */ ]
    }

| Errors | 4, 9, 16. |

8.5. Константа

ПараметрЗначение
URLPATCH B/Constant_<Имя>(guid'00000000-0000-0000-0000-000000000000')?$format=json
Body{"Value": <значение>}
NoteКлюч константы — всегда нулевой guid.

9. Удаление объекта (DELETE)

9.1. DELETE B/<Prefix>_<Имя>(<key>) — физическое удаление

ПараметрЗначение
URLB/Catalog_Товары(guid'value')
Headers(опц.) If-Match: <DataVersion>.
Body
Result204 No Content. Объект удаляется физически, не помечается.
Errors7, 9, 19, 20; 412 при несовпадении If-Match.

9.2. Пометка на удаление — НЕТ отдельного DELETE

Эквивалент «пометить на удаление» — PATCH с {"DeletionMark": true}. Это именно PATCH, не DELETE.


10. Действия документов: Post / Unpost

10.1. POST B/Document_<Имя>(<key>)/Post?PostingModeOperational=<bool>

ПараметрЗначение
URLB/Document_РасходТоваров(guid'value')/Post?PostingModeOperational=false
QueryPostingModeOperational (true — оперативное проведение, false — обычное).
Body
ResultДокумент в проведённом состоянии.
Errors9, 11 (если документ не поддерживает проведение), 19, 20, 21.
NoteЭто команда, а не флаг. PATCH с "Posted": true не работает.

10.2. POST B/Document_<Имя>(<key>)/Unpost

ПараметрЗначение
URLB/Document_РасходТоваров(guid'value')/Unpost
Query
Body
ResultДокумент в не проведённом состоянии.
Errors9, 11, 19, 20.

11. Виртуальные таблицы регистров (функции)

Общая форма: GET B/<RegisterPrefix>_<Имя>/<Function>(<params>)?<query>.

Параметры функции — пары Ключ=Значение, разделены запятыми. Если параметр сам — выражение отбора, его кавычат и используют обычный синтаксис $filter. Без $format=json отдадут atom-xml.

11.1. GET .../InformationRegister_<Имя>/SliceLast(...) / SliceFirst(...)

ПараметрЗначение
URLB/InformationRegister_КурсыВалют/SliceLast(Period=datetime'2008-01-01T00:00:00',Condition='Валюта_Key eq guid''value''')
ПараметрыPeriod (опц.) — точка среза; Condition (опц.) — отбор в синтаксисе $filter.
Query$select, $expand, $format.
ResultКоллекция строк регистра на момент среза.
Errors11 (нет такой функции на этом регистре), 12 (нет обязательного аргумента), 14 (в Condition), 20, 21.
NoteДля подчинённого регистра используется _RecordType: InformationRegister_КурсыВалют_RecordType/SliceLast?....

11.2. GET .../AccumulationRegister_<Имя>/Balance(...) / Turnovers(...) / BalanceAndTurnovers(...)

ПараметрЗначение
URLB/AccumulationRegister_ТоварныеЗапасы/BalanceAndTurnovers(StartPeriod=datetime'2014-01-01',EndPeriod=datetime'2014-02-01',Condition='Товар_Key eq guid''value''')
ПараметрыStartPeriod, EndPeriod, Condition. Конкретный набор зависит от функции.
ResultКоллекция итогов.

11.3. GET .../AccountingRegister_<Имя>/Balance(...)

ПараметрЗначение
URLB/AccountingRegister_Хозрасчетный/Balance(AccountCondition='Account_Key eq guid''value1''',Condition='ExtDimension1 eq cast(guid''value2'', ''Catalog_Контрагенты'')',ExtraDimensions='value3')?$format=json
ПараметрыAccountCondition, Condition, ExtraDimensions (зависят от функции).
ResultКоллекция строк остатков.

11.4. $expand на виртуальных таблицах

Поддерживается для измерений ($expand=Организация → расширит измерение Организация и положит его в свойство с ключом + объект). Для составных типов результат уезжает в свойство с суффиксом _Expanded. Это отклонение от OData v3 — учитывать в клиентском коде.


12. План обмена: SelectChanges / NotifyChangesReceived

12.1. POST B/SelectChanges?DataExchangePoint=<url>&MessageNo=<N>

ПараметрЗначение
URLB/SelectChanges?DataExchangePoint='B/ExchangePlan_ОбменДанными(guid'value')'&MessageNo=34
QueryDataExchangePoint (канонический URL элемента плана обмена, в одинарных кавычках), MessageNo (целое — номер сообщения, который будет проставлен).
Body
ResultТолько atom-feed. Поток atom-entry + atom-deleted-entry (RFC 6721). $format=json игнорируется — JSON-вариант не предусмотрен.
Errors8, 9 (узел плана обмена не найден), 14, 19, 20.
NoteДля парсинга нужен отдельный atom-xml ридер; стандартный json-парсер OData не сработает.

12.2. POST B/NotifyChangesReceived?DataExchangePoint=<url>&MessageNo=<N>

ПараметрЗначение
URLB/NotifyChangesReceived?DataExchangePoint='B/ExchangePlan_ОбменДанными(guid'value')'&MessageNo=34
QueryDataExchangePoint, MessageNo — те же, что в SelectChanges.
Body
ResultПодтверждение приёма; статус 200.
Errors8, 9, 14.

Приложение: матрица «endpoint × query-параметры»

Endpoint$filter$select$expand$orderby$top/$skip$inlinecount$countallowedOnly$format=json
GET /XML only
GET /$metadataXML only
GET /<Set>
GET /<Set>/$countselftext/plain
GET /<Set>(<key>)
GET /<Set>_<TP>
GET /<Reg>/<Func>(...)— (через Condition)✓ (с оговоркой)
POST /<Set>
PUT/PATCH /<Set>(<key>)
DELETE /<Set>(<key>)
POST .../Post//Unpost
POST /SelectChangesXML only
POST /NotifyChangesReceived