Как обновить табличную часть элемента через OData в 1С без ошибки 405?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При интеграции сторонних систем с 1С:Предприятием через стандартный интерфейс OData разработчики часто сталкиваются с ошибкой «Использованный HTTP метод запрещен в данном контексте» (HTTP 405 Method Not Allowed). Эта ошибка стабильно возникает при попытке отправить запрос метода PATCH, PUT или POST напрямую на URL табличной части объекта (например, к ресурсу вида .../odata/standard.odata/Document_ИмяДокумента_ТабличнаяЧасть).

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

Выясняем причину: почему возникает ошибка HTTP 405?

Для того чтобы понять природу ошибки, обратимся к объектной модели 1С. В системе 1С:Предприятие табличные части документов, справочников или бизнес-процессов не являются самостоятельными сущностями базы данных. Они не могут существовать в отрыве от своего владельца — родительского объекта (документа-ссылки или элемента справочника).

Стандартный интерфейс OData спроектирован в строгом соответствии с этой моделью:

  1. Доступ к табличным частям как к отдельным коллекциям разрешен только для чтения. Вы можете использовать метод GET с фильтрацией по Ref_Key (ссылке на владельца), чтобы быстро получить строки конкретного документа.
  2. Любые модификации данных (создание, изменение, удаление строк табличной части) могут производиться исключительно через родительский объект. Платформа 1С не поддерживает точечную запись в таблицу БД в обход бизнес-логики проведения и записи самого документа.

Таким образом, попытка выполнить PATCH напрямую к табличной части всегда будет отклонена сервером 1С с ошибкой 405.

Золотое правило OData 1С: Полная перезапись табличной части

Прежде чем перейти к написанию кода, необходимо усвоить важнейшее правило работы с коллекциями в OData 1С: табличная часть при отправке запроса PATCH или PUT на родительский объект всегда перезаписывается целиком.

Если в вашем документе в 1С сейчас находится 20 строк товаров, а вы отправляете PATCH-запрос к документу, передавая в JSON-пакете табличную часть только с 1 измененной строкой, то после выполнения запроса в документе останется ровно 1 строка. Остальные 19 строк будут безвозвратно удалены.

Из этого правила следует единственно верный алгоритм работы, известный как Read-Modify-Write (Чтение - Модификация - Запись):

  1. Отправляем GET-запрос к родительскому объекту, чтобы получить его текущее состояние и все строки его табличной части.
  2. Парсим полученный JSON на стороне клиентского приложения, находим нужные строки в массиве табличной части и вносим в них изменения (или добавляем новые строки).
  3. Отправляем измененный массив табличной части в составе общего JSON-объекта обратно на сервер методом PATCH, обращаясь к самому документу.

Разбираем по шагам: Практическая реализация алгоритма

Рассмотрим подробнее каждый этап реализации этого алгоритма. Представим, что нам необходимо обновить признак отмены или изменить количество в строках табличной части «Товары» документа «Заказ на перемещение».

Шаг 1. Получение текущего состояния объекта (GET)

Для начала нам необходимо прочитать объект из базы данных по его уникальному идентификатору (GUID). Мы должны сформировать правильный запрос к родительскому объекту. Посмотрим на пример кода на языке 1С:


// Сначала подготавливаем HTTP-соединение
Соединение = Новый HTTPСоединение("myserver.ru", 80, "Администратор", "Пароль");

// Формируем GET-запрос для получения всего документа вместе с ТЧ
ИмяДокумента = "Document_ЗаказНаПеремещение";
ИдентификаторОбъекта = "83a6f31f-e809-11e8-8cd3-50465d037106"; // GUID документа

АдресЗапроса = "ИмяБазы/odata/standard.odata/" + ИмяДокумента + "(guid'" + ИдентификаторОбъекта + "')?$format=json";
ЗапросGET = Новый HTTPЗапрос(АдресЗапроса);

ОтветGET = Соединение.ВызватьHTTPМетод("GET", ЗапросGET);
Если ОтветGET.КодСостояния = 200 Тогда
    СтрокаJSON = ОтветGET.ПолучитьТелоКакСтроку();
Иначе
    ВызватьИсключение "Не удалось получить объект: " + ОтветGET.КодСостояния;
КонецЕсли;

Шаг 2. Парсинг и модификация данных

Получив JSON-представление документа, мы преобразуем его в структуру 1С (или в объект вашей интеграционной системы, например, словарь в Python). Мы локально модифицируем строки табличной части, сохраняя при этом все остальные строки без изменений.

Проанализируем структуру полученного JSON. В нем обязательно будет присутствовать массив с именем вашей табличной части (например, "Товары"). Каждая строка в этом массиве содержит реквизиты строки ТЧ.

Важные требования OData к передаче табличных частей при записи:

Рассмотрим пример модификации данных на Python (часто используемом для написания внешних скриптов интеграции):


import requests

# Предположим, мы уже авторизовались и получили документ (GET)
url = "http://myserver.ru/ИмяБазы/odata/standard.odata/Document_ЗаказНаПеремещение(guid'83a6f31f-e809-11e8-8cd3-50465d037106')?$format=json"
response = requests.get(url, auth=('Администратор', 'Пароль'))
doc_data = response.json()

# Извлекаем табличную часть "Товары"
товары = doc_data.get('Товары', [])

# Нам нужно отменить строку с определенным кодом строки (например, КодСтроки = 3)
for row in товары:
    if row.get('КодСтроки') == 3:
        row['Отменено'] = True

# Подготавливаем JSON-структуру для отправки PATCH-запроса
# Мы передаем ТОЛЬКО то, что хотим обновить, но табличную часть передаем ЦЕЛИКОМ!
payload = {
    "Товары@odata.type": "Collection(StandardODATA.Document_ЗаказНаПеремещение_Товары_RowType)",
    "Товары": товары
}

Шаг 3. Запись измененного объекта (PATCH)

Теперь мы отправляем подготовленные данные обратно на сервер. Обратите внимание: запрос отправляется на URL родительского документа, а не его табличной части!

Ниже представлен пример отправки PATCH-запроса из встроенного языка 1С:


// Предположим, в переменной "НоваяТЧ" у нас находится массив измененных строк
// и мы сформировали JSON-тело запроса в переменной "ТелоЗапросаJSON"

ЗапросPATCH = Новый HTTPЗапрос(АдресЗапроса); // URL родительского документа
ЗапросPATCH.УстановитьТелоИзСтроки(ТелоЗапросаJSON, КодировкаТекста.UTF8);

// Устанавливаем необходимый заголовок для формата JSON
ЗапросPATCH.Заголовки.Вставить("Content-Type", "application/json");

ОтветPATCH = Соединение.ВызватьHTTPМетод("PATCH", ЗапросPATCH);

Если ОтветPATCH.КодСостояния = 200 Тогда
    ЗаписатьЛогСобытий("Табличная часть успешно обновлена!");
Иначе
    ВызватьИсключение "Ошибка при обновлении ТЧ через OData: " + ОтветPATCH.ПолучитьТелоКакСтроку();
КонецЕсли;

Защита от перезаписи данных (Оптимистическая блокировка)

Поскольку процесс обновления табличной части разбит на два шага (сначала GET, затем PATCH), существует вероятность возникновения конкурентного доступа. В период времени между тем, как ваше внешнее приложение прочитало документ, и тем, как оно отправило команду на изменение, другой пользователь в интерфейсе 1С мог изменить этот же документ.

Для предотвращения затирания чужих изменений в OData 1С встроен механизм оптимистической блокировки данных при помощи HTTP-заголовков ETag и If-Match:

  1. При выполнении GET-запроса сервер 1С возвращает в заголовке ответа параметр ETag (или свойство DataVersion в теле JSON). Это уникальный хэш текущей версии объекта в базе данных.
  2. При отправке PATCH-запроса вы обязаны передать полученный ETag в заголовке запроса If-Match: "ваша_версия_данных".
  3. Если документ за это время был изменен кем-то другим, сервер 1С вернет ошибку HTTP 412 Precondition Failed. Ваше приложение должно обработать эту ошибку, заново прочитать актуальные данные (GET), наложить свои изменения поверх новых данных и повторить попытку записи.

Если вы уверены, что хотите принудительно перезаписать данные независимо от изменений в системе, вы можете передать в заголовке значение звездочки: If-Match: *. Однако использовать этот подход рекомендуется с осторожностью.

Резюме

Давайте обобщим ключевые правила успешного изменения табличных частей 1С через OData REST API:

Следуя этим правилам, вы сможете построить стабильную, масштабируемую и бесконфликтную интеграцию ваших внешних информационных систем с решением на платформе 1С:Предприятие.

← На главную