При интеграции внешних систем с 1С через OData (стандартный интерфейс REST) разработчики часто сталкиваются с архитектурной особенностью платформы: важные данные, которые визуально относятся к объекту (например, статус оплаты счета), физически могут храниться в отдельном регистре сведений. Это создает проблему «N+1 запросов», когда для получения списка документов со статусами приходится делать отдельный вызов для каждого документа.
В этой статье мы подробно разберем, как эффективно извлекать такие данные, используя возможности протокола OData, не прибегая к изменению конфигурации 1С и не создавая лишней нагрузки на сервер.
Разберем ситуацию на примере документа СчетНаОплатуПокупателю. В типовых конфигурациях 1С статус этого документа (например, «Оплачен», «Частично оплачен») не является реквизитом самого документа. Платформа записывает эти данные в регистр сведений СтатусыДокументов. Это сделано для того, чтобы хранить историю изменений или быстро обновлять состояние без перезаписи «тяжелого» объекта документа.
Пытаясь получить данные через /odata/standard.odata/Document_СчетНаОплатуПокупателю, мы видим только стандартные поля: дату, номер, контрагента и т.д. Поля «Статус» там просто не существует. Рассмотрим, как решить эту задачу правильно.
Одной из самых частых ошибок является попытка «дотянуться» от документа к регистру. В архитектуре OData 1С навигационные свойства (связи) обычно строятся от записи регистра к объекту, на который она ссылается. Вместо того чтобы запрашивать список счетов, мы запросим записи регистра и попросим 1С «подтянуть» данные связанных документов.
Рассмотрим структуру запроса, который позволит за один раз получить и статус, и данные счета. Выясним, какое измерение в регистре InformationRegister_СтатусыДокументов отвечает за связь с документом (обычно это поле Объект или Документ).
Пример запроса:
GET /odata/standard.odata/InformationRegister_СтатусыДокументов?$expand=Объект&$filter=Объект_Type eq 'StandardODATA.Document_СчетНаОплатуПокупателю'
В этом примере мы используем параметр $expand. Он заставляет сервер включить в ответ не только идентификатор (GUID) счета, но и все его реквизиты. Параметр $filter с конструкцией _Type позволяет нам ограничить выборку только счетами на оплату, если регистр хранит статусы для разных типов документов.
Если регистр сведений является периодическим (т.е. хранит историю изменений статусов во времени), обычный запрос к InformationRegister_СтатусыДокументов вернет вам все записи за все время. Это избыточно. Для получения только актуального статуса в 1С предусмотрена виртуальная таблица СрезПоследних.
В интерфейсе OData она доступна через суффикс _SliceLast. Проанализируем, как это применить:
GET /odata/standard.odata/InformationRegister_СтатусыДокументов_SliceLast(Condition='...', Period=datetime'2023-10-27T00:00:00')?$expand=Объект
Использование SliceLast гарантирует, что для каждого счета вы получите только одну, самую свежую запись. Это кардинально снижает объем передаваемого трафика и упрощает обработку данных на стороне внешней системы.
Бывают ситуации, когда вам уже известен список GUID конкретных счетов (например, 100 штук), и вам нужно узнать их статусы. Вместо 100 отдельных GET-запросов, мы воспользуемся механизмом пакетирования. Пакетный запрос позволяет объединить множество команд в одном HTTP-запросе.
Рассмотрим пример формирования тела POST запроса к эндпоинту $batch:
--batch_id_1
Content-Type: application/http
Content-Transfer-Encoding: binary
GET InformationRegister_СтатусыДокументов_SliceLast?$filter=Объект_Key eq guid'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' HTTP/1.1
--batch_id_1
Content-Type: application/http
Content-Transfer-Encoding: binary
GET InformationRegister_СтатусыДокументов_SliceLast?$filter=Объект_Key eq guid'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy' HTTP/1.1
--batch_id_1--
Сервер 1С обработает все эти запросы внутри одной транзакции (или последовательно) и вернет один массив ответов. Это решает проблему сетевых задержек (Latency), которая является основной причиной медленной работы OData.
Одной из проблем, озвученных в теме, было отсутствие поля «Дата изменения» в регистре. Однако, если регистр периодический, у него всегда есть системное поле Period. Мы можем использовать его для получения только тех статусов, которые изменились за последнюю неделю:
GET /odata/standard.odata/InformationRegister_СтатусыДокументов?$filter=Period gt datetime'2023-10-20T00:00:00' and Объект_Type eq 'StandardODATA.Document_СчетНаОплатуПокупателю'
Если же регистр непериодический, и в него не добавили специальный ресурс «ДатаИзменения», то отфильтровать новые записи стандартными средствами OData будет невозможно. В этом случае придется либо модифицировать конфигурацию (добавлять реквизит с датой), либо использовать поле DataVersion.
DataVersion — это системное поле типа RowVersion, которое меняется при любом обновлении записи. Хотя оно не является датой в привычном понимании, внешняя система может запоминать максимальное значение DataVersion из предыдущей загрузки и запрашивать записи, где DataVersion больше известного значения.
При работе с большими объемами данных (например, если в базе сотни тысяч счетов), никогда не запрашивайте все данные сразу. Проанализируем использование параметров порционной загрузки:
$top для ограничения количества записей в одном ответе (например, по 1000).$skip для смещения при следующем запросе.$orderby, чтобы порядок записей был детерминированным, иначе пагинация может пропустить данные.Пример запроса первой тысячи записей:
GET /odata/standard.odata/InformationRegister_СтатусыДокументов?$orderby=Period&$top=1000&$skip=0&$expand=Объект
Мы выяснили, что отсутствие нужного поля в объекте 1С — это не тупик для OData. Основная стратегия заключается в том, чтобы найти регистр, в котором хранятся эти данные, и строить запрос от него к документу через $expand. Для тестирования и быстрого построения таких URL-адресов можно использовать генератор запросов OData.
Ключевые рекомендации:
_SliceLast) для получения актуальных данных._Type), если работаете с составными полями.ДатаМодификации в регистр значительно упростит жизнь разработчикам внешних систем.Следуя этим правилам, мы сможем построить надежный и быстрый обмен данными с 1С, используя исключительно стандартные средства платформы.