Содержание
- Сквозные query-параметры
- Сквозные HTTP-заголовки
- Сквозные коды ошибок
- Метаданные и discovery
- Чтение коллекций
- Чтение одиночных сущностей
- Создание объекта (POST)
- Обновление объекта (PUT/PATCH)
- Удаление объекта (DELETE)
- Действия документов: Post / Unpost
- Виртуальные таблицы регистров (функции)
- План обмена: SelectChanges / NotifyChangesReceived
1. Сквозные query-параметры
Применяются на нескольких endpoint'ах. В таблице — где разрешены.
| Параметр | Где работает | Что делает | Подводные камни |
$format=json | atom | GET почти везде | Формат тела ответа. По умолчанию atom-xml. Альтернатива — заголовок Accept. | На $metadata и SelectChanges игнорируется — всегда XML. |
$format=application/json;odata=minimalmetadata | nometadata | fullmetadata | GET сущности/коллекции в 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 | none | GET коллекции | Включает в ответ общее число записей (m:count в atom, odata.count в json). | Игнорирует $top и $skip — листать и считать одновременно нельзя. Считать отдельным запросом через /$count. |
$count (как часть пути, не query) | GET .../EntitySet/$count | Возвращает только число (text/plain). | См. §5. |
allowedOnly=true | GET коллекции только | Возвращает только записи, разрешённые ограничением доступа к данным. Без него запрос упадёт в 401 при попадании запрещённых строк в выборку. | Не работает на POST/PATCH/DELETE и на одиночных сущностях. |
Литералы в URL/значениях параметров
Edm.Guid → guid'8d04ce3c-6360-11e9-dd92-fa163e8ba11d'
Edm.DateTime → datetime'1945-05-09T06:00:00'
- Одинарные кавычки внутри строк экранируются удвоением:
'O''Brien'
- Кириллица в именах сущностей и реквизитов должна быть percent-encoded в URL (httpx делает это автоматически).
| Заголовок | Запросы | Назначение |
Accept: application/json | GET | Альтернатива $format=json. На $metadata и SelectChanges игнорируется. |
Content-Type: application/json | application/atom+xml | POST / PUT / PATCH | Формат тела запроса. |
If-Match: <DataVersion> | PATCH / DELETE | Оптимистическая блокировка. Значение — DataVersion, как пришло от 1С, opaque-строка (хранить сырьё, не пересоздавать). При несовпадении — 412 Precondition Failed, надо перечитать и повторить. |
1C_OData-DataLoadMode: true | POST / 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 | В объекте есть свойства с одинаковыми именами | инициализация интерфейса |
| 19 | HTTP-метод запрещён в данном контексте | PUT на коллекцию, POST на сущность и т. п. |
| 20 | Нет прав на действие, либо выборка попала под ограничение без allowedOnly | любой |
| 21 | Вызов нереализованной функции / неверные аргументы / нереализованная лямбда | функции/действия/$filter |
HTTP 5XX — ошибка на сервере, тело может быть произвольным.
Отдельные стандартные HTTP-коды:
401 — попадание под ограничение доступа к данным без allowedOnly=true (см. код 20).
404 — ресурс/сущность не найдены (часто сопровождается кодом 9 или 8).
412 — If-Match не совпал с текущим DataVersion.
501 — попытка прочитать тип, который OData не поддерживает (см. odata.md §17.4.3).
4.1. GET B/ — упрощённое описание (список сущностей)
| Параметр | Значение |
| Метод | GET |
| URL | B/ |
| Path / query | — |
| Headers | Accept: application/xml (по умолчанию). |
| Body | — |
| Result | Список доступных сущностей, XML-документ (atom service document). |
| Errors | 4XX, 5XX без специфики. |
| Параметр | Значение |
| Метод | GET |
| URL | B/$metadata |
| Body | — |
| Result | XML 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. |
| Headers | Accept. |
| Result (json) | {"odata.metadata": "...", "value": [{...}, ...]} (+ odata.count при $inlinecount=allpages). |
| Result (atom) | <feed> с <entry> (+ <m:count> при $inlinecount). |
| Errors | 8 (тип не найден), 14 ($filter/$orderby), 20/401 (без allowedOnly и ограничение доступа), 21 (нереализованные функции). |
5.2. GET B/<Prefix>_<Имя>_<ТабличнаяЧасть> — все строки табличных частей всех объектов
| Параметр | Значение |
| URL | B/Document_РасходТовара_Товары |
| Query | те же что в 5.1; но $expand тут не поддерживается (расширение реквизитов табличных частей не работает). |
| Result | Коллекция строк табличных частей (читаемая). |
| Errors | 14, 20/401. |
5.3. GET B/<Prefix>_<Имя>/$count — только количество
| Параметр | Значение |
| URL | B/Catalog_Товары/$count?$filter=Цена gt 500 |
| Query | $filter (другие игнорируются). |
| Result | text/plain с числом, например 19. |
| Errors | 14 ($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). |
| Errors | 7 (неполный составной ключ), 8, 9 (не найден → 404), 10 ($select несуществующего поля), 20/401. |
6.2. GET B/<Prefix>_<Имя>_RowType(Ref_Key=guid'...', LineNumber=N) — строка табличной части
| Параметр | Значение |
| URL | B/Document_РасходТовара_Товары(Ref_Key=guid'value', LineNumber=1) |
| Ключ | Композитный, оба компонента обязательны. |
| Result | Одна строка табличной части (только чтение). |
| Errors | 7, 9. |
| Note | Прямого PATCH/DELETE на строку нет — табличные части пишутся целиком через PATCH родителя (§8.3). |
| Параметр | Значение |
| URL | B/InformationRegister_ПриходныеЦены(Recorder_Key=guid'value') |
| Result | Набор записей, привязанных к регистратору. RecordSet — массив. |
| Errors | 7, 9. |
| Параметр | Значение |
| URL | B/InformationRegister_ПриходныеЦены_RecordType(Товар_Key=guid'value', ТипЦены='Приходная') |
| Ключ | Все измерения регистра + (для подчинённых) Recorder_Key. |
| Result | Одна запись регистра, только чтение. |
| Errors | 7, 9. |
6.5. GET B/AccountingRegister_<Имя>(<Recorder_Key>) — набор записей регистра бухгалтерии по регистратору
| Параметр | Значение |
| URL | B/AccountingRegister_РегистрБухгалтерииCКорреспонденцией(guid'value')?$format=json |
| Result | json-документ с Recorder_Key и массивом RecordSet. |
| Note | Используется как первый шаг для PATCH набора записей регистра бухгалтерии (§8.4). |
6.6. Dot-traversal: GET B/<Prefix>_<Имя>(<key>)/<Реквизит>/<Свойство>
| Параметр | Значение |
| URL | B/Document_РасходТовара(guid'value')/Валюта/Description |
| Назначение | Получить значение свойства связанной сущности по цепочке через /. |
| Note | Для стандартных реквизитов в цепочке использовать только английские имена (Description, Code, ...). |
| Errors | 9, 10, 7. |
7. Создание объекта (POST)
7.1. POST B/<Prefix>_<Имя> — создать сущность
| Параметр | Значение |
| URL | URL набора сущностей: B/Catalog_Товары |
| Headers | Content-Type: application/json или application/atom+xml; (опц.) 1C_OData-DataLoadMode: true. |
| Body (json) | Объект со значениями полей. Ссылочные поля — через <Реквизит>_Key. Лишние поля молча игнорируются. |
| Body (atom) | <entry> с <m:properties>. Категория задаётся через <category term="StandardODATA.Catalog_<Имя>" .../>. |
| Result | 201 Created; Location: B/<Prefix>_<Имя>(guid'<новый-Ref_Key>'); тело — сущность с проставленными Ref_Key, DataVersion. |
| Errors | 4 (неверное значение), 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>) — частичное обновление
| Параметр | Значение |
| URL | B/Catalog_Магазины(guid'value')?$format=json |
| Headers | Content-Type; (опц.) If-Match: <DataVersion>; (опц.) 1C_OData-DataLoadMode: true. |
| Body | Только изменяемые поля. Пропущенные поля сохраняются. |
| Result | Обновлённая сущность (либо 204 No Content, в зависимости от конфигурации). |
| Errors | 4, 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 родителя
| Параметр | Значение |
| URL | PATCH B/Catalog_Магазины(guid'value')?$format=json |
| Body | Помимо обычных полей: "<ТЧ>@odata.type": "Collection(StandardODATA.<Prefix>_<Имя>_<ТЧ>_RowType)" + "<ТЧ>": [ {LineNumber, ...}, ... ]. |
| Critical | Передавать весь массив строк, даже если меняется одна. Частичный апдейт строки невозможен. |
| Errors | 13 (попытка прямого POST на _<ТЧ>), 4, 16. |
8.4. Набор записей регистра бухгалтерии — read → modify → PATCH
Последовательность:
GET B/AccountingRegister_<Имя>(guid'<Recorder>')?$format=json — прочитать текущий набор.
- Модифицировать массив
RecordSet локально (изменение/добавление/удаление строк).
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. Константа
| Параметр | Значение |
| URL | PATCH B/Constant_<Имя>(guid'00000000-0000-0000-0000-000000000000')?$format=json |
| Body | {"Value": <значение>} |
| Note | Ключ константы — всегда нулевой guid. |
9. Удаление объекта (DELETE)
9.1. DELETE B/<Prefix>_<Имя>(<key>) — физическое удаление
| Параметр | Значение |
| URL | B/Catalog_Товары(guid'value') |
| Headers | (опц.) If-Match: <DataVersion>. |
| Body | — |
| Result | 204 No Content. Объект удаляется физически, не помечается. |
| Errors | 7, 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>
| Параметр | Значение |
| URL | B/Document_РасходТоваров(guid'value')/Post?PostingModeOperational=false |
| Query | PostingModeOperational (true — оперативное проведение, false — обычное). |
| Body | — |
| Result | Документ в проведённом состоянии. |
| Errors | 9, 11 (если документ не поддерживает проведение), 19, 20, 21. |
| Note | Это команда, а не флаг. PATCH с "Posted": true не работает. |
10.2. POST B/Document_<Имя>(<key>)/Unpost
| Параметр | Значение |
| URL | B/Document_РасходТоваров(guid'value')/Unpost |
| Query | — |
| Body | — |
| Result | Документ в не проведённом состоянии. |
| Errors | 9, 11, 19, 20. |
11. Виртуальные таблицы регистров (функции)
Общая форма: GET B/<RegisterPrefix>_<Имя>/<Function>(<params>)?<query>.
Параметры функции — пары Ключ=Значение, разделены запятыми. Если параметр сам — выражение отбора, его кавычат и используют обычный синтаксис $filter. Без $format=json отдадут atom-xml.
| Параметр | Значение |
| URL | B/InformationRegister_КурсыВалют/SliceLast(Period=datetime'2008-01-01T00:00:00',Condition='Валюта_Key eq guid''value''') |
| Параметры | Period (опц.) — точка среза; Condition (опц.) — отбор в синтаксисе $filter. |
| Query | $select, $expand, $format. |
| Result | Коллекция строк регистра на момент среза. |
| Errors | 11 (нет такой функции на этом регистре), 12 (нет обязательного аргумента), 14 (в Condition), 20, 21. |
| Note | Для подчинённого регистра используется _RecordType: InformationRegister_КурсыВалют_RecordType/SliceLast?.... |
11.2. GET .../AccumulationRegister_<Имя>/Balance(...) / Turnovers(...) / BalanceAndTurnovers(...)
| Параметр | Значение |
| URL | B/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(...)
| Параметр | Значение |
| URL | B/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>
| Параметр | Значение |
| URL | B/SelectChanges?DataExchangePoint='B/ExchangePlan_ОбменДанными(guid'value')'&MessageNo=34 |
| Query | DataExchangePoint (канонический URL элемента плана обмена, в одинарных кавычках), MessageNo (целое — номер сообщения, который будет проставлен). |
| Body | — |
| Result | Только atom-feed. Поток atom-entry + atom-deleted-entry (RFC 6721). $format=json игнорируется — JSON-вариант не предусмотрен. |
| Errors | 8, 9 (узел плана обмена не найден), 14, 19, 20. |
| Note | Для парсинга нужен отдельный atom-xml ридер; стандартный json-парсер OData не сработает. |
12.2. POST B/NotifyChangesReceived?DataExchangePoint=<url>&MessageNo=<N>
| Параметр | Значение |
| URL | B/NotifyChangesReceived?DataExchangePoint='B/ExchangePlan_ОбменДанными(guid'value')'&MessageNo=34 |
| Query | DataExchangePoint, MessageNo — те же, что в SelectChanges. |
| Body | — |
| Result | Подтверждение приёма; статус 200. |
| Errors | 8, 9, 14. |
Приложение: матрица «endpoint × query-параметры»
| Endpoint | $filter | $select | $expand | $orderby | $top/$skip | $inlinecount | $count | allowedOnly | $format=json |
GET / | — | — | — | — | — | — | — | — | XML only |
GET /$metadata | — | — | — | — | — | — | — | — | XML only |
GET /<Set> | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ |
GET /<Set>/$count | ✓ | — | — | — | — | — | self | ✓ | text/plain |
GET /<Set>(<key>) | — | ✓ | — | — | — | — | — | — | ✓ |
GET /<Set>_<TP> | ✓ | ✓ | — | ✓ | ✓ | ✓ | — | ✓ | ✓ |
GET /<Reg>/<Func>(...) | — (через Condition) | ✓ | ✓ (с оговоркой) | — | — | — | — | — | ✓ |
POST /<Set> | — | — | — | — | — | — | — | — | ✓ |
PUT/PATCH /<Set>(<key>) | — | — | — | — | — | — | — | — | ✓ |
DELETE /<Set>(<key>) | — | — | — | — | — | — | — | — | — |
POST .../Post//Unpost | — | — | — | — | — | — | — | — | — |
POST /SelectChanges | — | — | — | — | — | — | — | — | XML only |
POST /NotifyChangesReceived | — | — | — | — | — | — | — | — | — |