При организации интеграции между 1С и внешними системами (сайтами, мобильными приложениями, внешними сервисами) часто возникает задача регулярной передачи не всей базы целиком, а только измененных данных. Если для справочников и документов логика интуитивно понятна, то работа с регистрами накопления имеет ряд тонкостей. Разберем по шагам, как правильно соединить веб-сервис и механизм планов обмена для надежной фиксации и выгрузки накопившихся движений.
Первое, на что необходимо обратить внимание, — принципиальное отличие регистров накопления от независимых регистров сведений. Регистры накопления всегда подчинены регистратору. Это означает, что система физически не может зарегистрировать изменение одной конкретной строки движения в разрезе ее измерений.
В таблице изменений регистра накопления фиксируется только ссылка на документ Регистратор, узел плана обмена и служебный номер сообщения. Проанализируем ключевые следствия этого механизма:
РегистрНакопленияНаборЗаписей целиком по данному регистратору.ПланыОбмена.ЗарегистрироватьИзменения(), куда передается набор с отбором по документу. Чтение самого набора из базы перед регистрацией выполнять не требуется.Веб-сервисы работают по протоколу без сохранения состояния (stateless). Чтобы избежать потери данных при сетевых сбоях, удалять регистрацию изменений в момент формирования ответа нельзя. Необходимо использовать двухфазный обмен с квитированием (подтверждением приема):
НомерПринятого). 1С очищает регистрацию только тех изменений, которые вошли в указанный подтвержденный пакет.ЗаписьСообщенияОбмена, что автоматически инкрементирует номер сообщения, выбирает текущие изменения и связывает их с новым номером.Рассмотрим пример операции веб-сервиса, которая принимает идентификатор узла и данные от внешнего устройства, удаляет подтвержденные изменения и возвращает порцию новых зарегистрированных движений.
Функция ВыполнитьОбмен(КодУстройства, НомерПринятогоСообщения) Экспорт
УстановитьПривилегированныйРежим(Истина);
ПланОбменаМенеджер = ПланыОбмена.ОбменСВнешнимиСистемами;
// 1. Поиск или инициализация узла обмена
УзелОбмена = ПланОбменаМенеджер.НайтиПоКоду(КодУстройства);
Если УзелОбмена.Пустая() Тогда
ВызватьИсключение "Узел обмена с кодом " + КодУстройства + " не найден!";
КонецЕсли;
// 2. Квитирование: удаление ранее успешно принятых данных
Если НомерПринятогоСообщения > 0 Тогда
ПланыОбмена.УдалитьРегистрациюИзменений(УзелОбмена, НомерПринятогоСообщения);
КонецЕсли;
// 3. Формирование сообщения с новыми изменениями
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку("UTF-8");
ЗаписьXML.ЗаписатьОбъявлениеXML();
ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения;
ЗаписьСообщения.НачатьЗапись(ЗаписьXML, УзелОбмена);
// Запись пространств имен для корректной сериализации
ЗаписьXML.ЗаписатьСоответствиеПространстваИмен("v8", "http://v8.1c.ru/data");
// Выбираем изменения, привязанные к новому номеру сообщения
ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(УзелОбмена, ЗаписьСообщения.НомерСообщения);
Пока ВыборкаИзменений.Следующий() Цикл
Данные = ВыборкаИзменений.Получить();
// Данные представляют собой РегистрНакопленияНаборЗаписей
ЗаписатьXML(ЗаписьXML, Данные);
КонецЦикла;
ЗаписьСообщения.ЗакончитьЗапись();
РезультатXML = ЗаписьXML.Закрыть();
// Возвращаем результат вызывающей стороне
Возврат РезультатXML;
КонецФункции
Стандартный метод ПланыОбмена.ВыбратьИзменения() сериализует весь набор записей в платформенный XML. Однако внешним сервисам и веб-сайтам зачастую не требуется структура 1С со служебными тегами — им нужен плоский список строк или JSON. В таком случае мы можем обратиться напрямую к таблице изменений через язык запросов 1С.
Посмотрим на пример запроса, который соединяет зарегистрированные изменения с реальными записями регистра накопления:
ВЫБРАТЬ
Движения.Период КАК Период,
Движения.Регистратор КАК Регистратор,
Движения.Номенклатура КАК Номенклатура,
Движения.Склад КАК Склад,
Движения.ВидДвижения КАК ВидДвижения,
Движения.Количество КАК Количество
ИЗ
РегистрНакопления.ОстаткиНоменклатуры.Изменения КАК ИзмененияРегистра
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиНоменклатуры КАК Движения
ПО ИзмененияРегистра.Регистратор = Движения.Регистратор
ГДЕ
ИзмененияРегистра.Узел = &УзелОбмена
И ИзмененияРегистра.НомерСообщения ЕСТЬ NULL
Проанализируем работу этого запроса. Поле НомерСообщения ЕСТЬ NULL указывает на данные, которые еще не были включены ни в один пакет передачи. Полученную выборку можно легко обойти циклом, сформировать массив структур и сериализовать в привычный для веб-разработчиков JSON через ЗаписьJSON.
Если регистр накопления активно наполняется документами, попытка выбрать все изменения разом может привести к падению рабочего процесса по нехватке памяти или к обрыву соединения веб-сервера по таймауту. Чтобы избежать этого, применим следующие рекомендации:
ВЫБРАТЬ ПЕРВЫЕ N в запросе по уникальным регистраторам, отдавая данные пачками по 500–1000 документов.Авторегистрация = Запретить.ПриЗаписи набора записей регистра. В обработчике проверяйте нужные условия фильтрации и вызывайте метод ПланыОбмена.ЗарегистрироватьИзменения() только тогда, когда движения действительно имеют отношение к сайту. Это защитит таблицу изменений от разрастания лишней информацией.