Приветствуем коллег-разработчиков! В этой статье мы подробно разберем одну очень коварную техническую проблему, связанную с совместной работой механизмов распределенных информационных баз (РИБ) и кастомных оперативных обменов через XML-файлы. Данная ситуация часто встречается в розничной торговле, когда из-за частых транзакций и блокировок базы при пробитии чеков разработчики пытаются настроить «гибридный» обмен: оперативные данные передаются файлами XML, а тяжелые данные синхронизируются раз в сутки через стандартный РИБ.
Рассмотрим сценарий: у нас есть Главный узел и подчиненный (кассовый) узел. Из-за блокировок СУБД во время пробития чеков ККМ было принято решение выгружать документы чеков из кассы в XML-файлы каждые 15 минут, а в Главном узле загружать их программно, очищая движения и снимая регистрацию изменений по документу. Ночью запускается стандартный РИБ. В результате мы сталкиваемся с проблемой: документы в Главном узле остаются проведенными, но их движения по регистрам полностью исчезают, хотя в дочернем узле все движения на месте. При этом, если документ перепровести в дочернем узле повторно, после следующего ночного обмена движения в Главном узле чудесным образом появляются. Давайте разберем по шагам, почему это происходит и как решить проблему.
Для того чтобы понять суть проблемы, нам необходимо заглянуть под капот платформы 1С и вспомнить, как именно регистрируются изменения в планах обмена. Многие разработчики ошибочно полагают, что документ и его движения — это единое целое в контексте обмена данными. Это не так.
Документы и их движения (наборы записей регистров накопления, бухгалтерии, сведений и т.д.) регистрируются на узлах планов обмена независимо друг от друга. Данные о регистрации изменений хранятся в отдельных системных таблицах БД (таблицах изменений):
Документ.ЧекККМ.Изменения.РегистрНакопления.Продажи.Изменения или РегистрНакопления.Запасы.Изменения.Когда вы в Главном узле принудительно очищаете движения импортированного из XML документа (например, через Движения.Записать()) или перепроводите его без формирования движений, платформа 1С автоматически фиксирует это действие. В таблицах изменений регистров для подчиненного узла регистрируется пустой набор записей.
Вызов метода ПланыОбмена.УдалитьРегистрациюИзменений(УзелСсылка, ДокОбъект) удаляет регистрацию изменений только для самого документа. Зарегистрированные пустые наборы записей по регистрам при этом остаются в базе Главного узла нетронутыми.
При запуске ночного обмена РИБ возникает классическая коллизия:
Почему перепроведение документа в подчиненном узле решает проблему при повторном обмене? Потому что после первой ночной сессии обмена квитанция о приеме данных снимает «зависшую» регистрацию пустых движений в Главном узле. Коллизия исчезает. При повторном перепроведении в дочернем узле движения заново регистрируются и при следующем обмене успешно заходят в базу без конфликтов.
---
Самый правильный технический путь решения проблемы «в лоб» — при ручной очистке движений документа в Главном узле удалять регистрацию изменений не только самого документа, но и всех связанных с ним регистров. Для этого нам потребуется обходить коллекцию движений документа и передавать структуру отбора по регистратору в метод ПланыОбмена.УдалитьРегистрациюИзменений().
Рассмотрим пример кода, который необходимо выполнять в Главном узле после программного создания или изменения документа:
// Очищаем регистрацию изменений для самого документа
ПланыОбмена.УдалитьРегистрациюИзменений(УзелСсылка, ДокОбъект.Ссылка);
// Нам также нужно очистить регистрацию изменений по каждому регистру, где документ оставил след
Для Каждого НаборЗаписей Из ДокОбъект.Движения Цикл
// Формируем структуру отбора по регистратору
ОтборРегистратора = Новый Структура("Регистратор", ДокОбъект.Ссылка);
// Удаляем регистрацию изменений набора записей для конкретного узла
ПланыОбмена.УдалитьРегистрациюИзменений(УзелСсылка, НаборЗаписей.Метаданные(), ОтборРегистратора);
КонецЦикла;
Этот простой шаг предотвратит появление пустых зарегистрированных изменений в таблицах регистрации регистров Главного узла, и при ночном обмене РИБ движения из дочернего узла придут без коллизий.
---
Другая распространенная ошибка — запуск кастомных алгоритмов очистки или изменения движений во время самого процесса штатного обмена РИБ. Если ваш код очистки написан в модуле документа (в обработчиках событий ПередЗаписью или ПриЗаписи) или в подписках на события, обязательно проверяйте режим загрузки данных.
Во время работы РИБ платформа устанавливает свойство ОбменДанными.Загрузка = Истина. Вся кастомная логика должна быть заблокирована при загрузке данных обмена:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Если происходит загрузка данных обмена, не вмешиваемся в движения!
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Ваш кастомный код очистки или модификации движений для ручного импорта
...
КонецПроцедуры
---
Если вы хотите уйти от сложного «костыльного» механизма параллельного обмена XML и ночного РИБ, рассмотрите более надежные архитектурные паттерны:
Используйте предложенные подходы, чтобы сделать ваши интеграционные механизмы в 1С стабильными и производительными!