Skill v1.0.1
Automated scan100/100+1 new
version: "1.0.1" name: data-exchange description: "Use for реализации и диагностики обмена данными 1С (РИБ, КД 2.0/3.0, EnterpriseData, БСП). Helps выбрать модель обмена, обеспечить идемпотентность пакетов и явное разрешение конфликтов."
Обмен данными 1С
Ключевой принцип: Обмен данными — это распределённая система. Каждый пакет должен быть идемпотентным (повторная загрузка не портит данные), каждый конфликт — явно разрешённым, каждая ошибка — залогированной с привязкой к узлу и номеру сообщения.
Правило 1: Архитектура планов обмена — выбор модели
Перед реализацией необходимо определить модель обмена. Выбор влияет на весь дальнейший код.
| Модель | Когда использовать | Механизм | |
|---|---|---|---|
| РИБ (распределённая ИБ) | Полная копия конфигурации на узлах, переносятся все данные | ПланыОбмена, XML-сериализация, ОбменДаннымиXML | |
| Избирательный обмен (БСП) | Обмен по правилам, фильтрация объектов, разные конфигурации | Подсистема «Обмен данными» БСП, формат EnterpriseData | |
| КД 2.0 | Сложные правила конвертации между разными конфигурациями | Обработка «Конвертация данных», XML-правила | |
| КД 3.0 / EDT | Современные проекты, правила на BSL, поддержка EnterpriseData | Конфигурация «Конвертация данных 3» |
Структура плана обмена
// Главный узел — это текущая ИБ// Подчинённые узлы — внешние базы данныхУзелГлавный = ПланыОбмена.МойОбмен.ГлавныйУзел();Если УзелГлавный = Неопределено Тогда// Это главный узел (центральная база)КонецЕсли;// Создание нового узла обменаНовыйУзел = ПланыОбмена.МойОбмен.СоздатьУзел();НовыйУзел.Код = "ФИЛИАЛ_001";НовыйУзел.Наименование = "Филиал Москва";НовыйУзел.Записать();
Правило 2: Регистрация изменений — ЗарегистрироватьИзменения
Регистрация изменений — основной механизм отслеживания того, что нужно передать узлу. Неправильная регистрация — главный источник ошибок: либо данные не уходят, либо уходят лишние.
Явная регистрация
// Зарегистрировать конкретный объект для конкретного узлаПланыОбмена.ЗарегистрироватьИзменения(УзелОбмена, ОбъектСсылка);// Зарегистрировать набор объектов (например, при первичной синхронизации)МассивОбъектов = Новый Массив;МассивОбъектов.Добавить(НоменклатураСсылка1);МассивОбъектов.Добавить(НоменклатураСсылка2);ПланыОбмена.ЗарегистрироватьИзменения(УзелОбмена, МассивОбъектов);
Подписки на события для автоматической регистрации
// В обработчике подписки ПриЗаписи — регистрировать изменения явноПроцедура НоменклатураПриЗаписи(Источник, Отказ, РежимЗаписи, РежимПроведения)Если Источник.ОбменДанными.Загрузка ТогдаВозврат; // Не регистрировать изменения при загрузке из обменаКонецЕсли;Для Каждого УзелОбмена Из ПланыОбмена.МойОбмен.Выбрать() ЦиклЕсли УзелОбмена.Ссылка <> ПланыОбмена.МойОбмен.ЭтотУзел() ТогдаПланыОбмена.ЗарегистрироватьИзменения(УзелОбмена.Ссылка, Источник.Ссылка);КонецЕсли;КонецЦикла;КонецПроцедуры
Критическое правило: проверка флага Загрузка
// ОБЯЗАТЕЛЬНО в начале каждого обработчика объектаЕсли Источник.ОбменДанными.Загрузка ТогдаВозврат; // Пропустить все проверки и дополнительную логику при загрузкеКонецЕсли;
Без этой проверки: при загрузке данных из внешней базы сработают бизнес-правила текущей базы → данные испортятся или загрузка завершится с ошибкой.
Правило 3: Выгрузка данных — формирование пакета сообщения
Канонический паттерн выгрузки (РИБ)
Функция СформироватьСообщениеОбмена(УзелОбмена) ЭкспортСообщениеОбмена = ПланыОбмена.СоздатьСообщениеОбмена();СообщениеОбмена.Отправитель = ПланыОбмена.МойОбмен.ЭтотУзел();СообщениеОбмена.Получатель = УзелОбмена;ЗаписьXML = Новый ЗаписьXML;ЗаписьXML.УстановитьСтроку();ПопыткаОбменДаннымиXML.НачатьЗаписьСообщения(ЗаписьXML, СообщениеОбмена);// Выгрузка измененийВыборкаИзменений = ПланыОбмена.Выбрать(УзелОбмена, ТипПланаОбмена.ТолькоИзменения);Пока ВыборкаИзменений.Следующий() ЦиклОбменДаннымиXML.ЗаписатьИзменения(ЗаписьXML, ВыборкаИзменений.ПолучитьОбъект());КонецЦикла;ОбменДаннымиXML.ЗакончитьЗаписьСообщения(ЗаписьXML);ИсключениеЗаписьЖурналаРегистрации(НСтр("ru = 'ОбменДанными.Выгрузка'"),УровеньЖурналаРегистрации.Ошибка,Метаданные.ПланыОбмена.МойОбмен,УзелОбмена,ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));ВызватьИсключение;КонецПопытки;Возврат ЗаписьXML.Закрыть();КонецФункции
Номер сообщения и подтверждение
// ВАЖНО: номер сообщения фиксируется только после успешной доставки// Не вызывать УстановитьНомерОтправленного до подтверждения получения// На стороне отправителя — после подтверждения доставки:ПланыОбмена.УстановитьНомерОтправленного(УзелОбмена, НомерСообщения);// На стороне получателя — после успешной загрузки:ПланыОбмена.УстановитьНомерПринятого(УзелОбмена, НомерСообщения);
Правило 4: Загрузка данных — идемпотентность и защита от дублей
Идемпотентность пакета означает: повторная загрузка одного и того же сообщения не изменяет результат. Это критично при сетевых сбоях и ручных повторных запусках.
Канонический паттерн загрузки (РИБ)
Процедура ЗагрузитьСообщениеОбмена(ТекстСообщения) ЭкспортЧтениеXML = Новый ЧтениеXML;ЧтениеXML.УстановитьСтроку(ТекстСообщения);СообщениеОбмена = ПланыОбмена.СоздатьСообщениеОбмена();НачатьТранзакцию();ПопыткаОбменДаннымиXML.НачатьЧтениеСообщения(ЧтениеXML, СообщениеОбмена);// Проверка: не загружать уже принятое сообщение (идемпотентность)Если СообщениеОбмена.НомерСообщения <= ПланыОбмена.МойОбмен.НайтиПоКоду(СообщениеОбмена.Отправитель.Код).НомерПринятого ТогдаЗаписьЖурналаРегистрации(НСтр("ru = 'ОбменДанными.Загрузка'"),УровеньЖурналаРегистрации.Предупреждение,,,СтрШаблон(НСтр("ru = 'Сообщение №%1 от узла %2 уже было принято. Пропускаем.'"),СообщениеОбмена.НомерСообщения, СообщениеОбмена.Отправитель));ОтменитьТранзакцию();Возврат;КонецЕсли;// Загрузка объектовПока ОбменДаннымиXML.ЧитатьИзменения(ЧтениеXML) ЦиклДанные = ОбменДаннымиXML.ПрочитатьИзменения(ЧтениеXML);Данные.ОбменДанными.Загрузка = Истина;Данные.Записать();КонецЦикла;ОбменДаннымиXML.ЗакончитьЧтениеСообщения(ЧтениеXML);// Фиксируем номер принятого сообщенияПланыОбмена.УстановитьНомерПринятого(СообщениеОбмена.Отправитель, СообщениеОбмена.НомерСообщения);ЗафиксироватьТранзакцию();ИсключениеОтменитьТранзакцию();ЗаписьЖурналаРегистрации(НСтр("ru = 'ОбменДанными.Загрузка'"),УровеньЖурналаРегистрации.Ошибка,Метаданные.ПланыОбмена.МойОбмен,СообщениеОбмена.Отправитель,СтрШаблон(НСтр("ru = 'Ошибка загрузки сообщения №%1.|%2'"), СообщениеОбмена.НомерСообщения,ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())));ВызватьИсключение;КонецПопытки;КонецПроцедуры
Обеспечение идемпотентности при загрузке справочников
// При загрузке справочников — искать по внешнему идентификатору, не создавать дублиФункция НайтиИлиСоздатьНоменклатуру(ВнешнийКод, Наименование)// Поиск по внешнему коду (синхронизационному ключу)СуществующийЭлемент = Справочники.Номенклатура.НайтиПоКоду(ВнешнийКод);Если СуществующийЭлемент <> Справочники.Номенклатура.ПустаяСсылка() ТогдаВозврат СуществующийЭлемент;КонецЕсли;// Создать новый только если не нашлиНовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();НовыйЭлемент.Код = ВнешнийКод;НовыйЭлемент.Наименование = Наименование;НовыйЭлемент.ОбменДанными.Загрузка = Истина;НовыйЭлемент.Записать();Возврат НовыйЭлемент.Ссылка;КонецФункции
Правило 5: Разрешение конфликтов
Конфликт возникает когда один и тот же объект изменён в двух узлах одновременно. Стратегия разрешения должна быть задокументирована и реализована явно — «молчаливая» победа последнего изменения недопустима в большинстве бизнес-задач.
Стратегии разрешения
| Стратегия | Когда применять | Реализация | |
|---|---|---|---|
| Победа главного узла | Главная база — источник правды | Отклонять изменения из подчинённых при конфликте | |
| Победа последнего изменения | Некритичные данные (настройки, описания) | МоментВремени() — кто новее, тот побеждает | |
| Победа по бизнес-правилу | Статусная машина, приоритеты | Явная функция разрешения | |
| Ручное разрешение | Критичные данные, не поддаются автоматике | Фиксировать в реестре конфликтов |
Паттерн: разрешение по приоритету узла
// В обработчике ОбменДанными.ОбработкаКонфликта (БСП)// или в ручной логике загрузки:Функция РазрешитьКонфликт(ДанныеПриемника, ДанныеИсточника, УзелОбмена) Экспорт// Если это главный узел — наши данные побеждают всегдаЕсли ПланыОбмена.МойОбмен.ГлавныйУзел() = Неопределено Тогда// Мы главный узелЗаписьЖурналаРегистрации(НСтр("ru = 'ОбменДанными.Конфликт'"),УровеньЖурналаРегистрации.Предупреждение,ДанныеПриемника.Метаданные(),ДанныеПриемника.Ссылка,СтрШаблон(НСтр("ru = 'Конфликт с узлом %1. Победа главного узла.'"), УзелОбмена));Возврат Ложь; // Не загружать данные источникаКонецЕсли;// Разрешение по дате последнего измененияЕсли ДанныеИсточника.МоментВремени() > ДанныеПриемника.МоментВремени() ТогдаВозврат Истина; // Загрузить — источник новееИначеЗаписьЖурналаРегистрации(НСтр("ru = 'ОбменДанными.Конфликт'"),УровеньЖурналаРегистрации.Предупреждение,ДанныеПриемника.Метаданные(),ДанныеПриемника.Ссылка,НСтр("ru = 'Конфликт. Победа приёмника — локальная версия новее.'"));Возврат Ложь; // Не загружать — наши данные новееКонецЕсли;КонецФункции
Правило 6: Обмен через подсистему БСП «Обмен данными»
БСП предоставляет готовую инфраструктуру: настройки узлов, правила обмена, транспорт (файл, FTP, e-mail, WS), очередь сообщений, реестр конфликтов.
Ключевые объекты подсистемы
// Основной модуль подсистемы// ОбменДаннымиСервер — серверный модуль// ОбменДаннымиКлиент — клиентский модуль// ОбменДанными — общий модуль (клиент-сервер)// Регистрация объекта для обмена через БСПОбменДаннымиСервер.ЗарегистрироватьОбъектДляОбмена(УзелОбмена, ОбъектСсылка);// Запуск синхронизации через БСПНастройкиОбмена = ОбменДаннымиСервер.НастройкиОбменаДляУзла(УзелОбмена);ОбменДаннымиСервер.СинхронизироватьДанные(НастройкиОбмена);
Формат EnterpriseData
EnterpriseData — стандартизированный XML-формат для обмена между конфигурациями 1С. Используется в современных планах обмена БСП.
// Структура пакета EnterpriseData// <Файл>// <ТипФайла>ОбменДанными</ТипФайла>// <ФорматВерсии>1.0</ФорматВерсии>// <Отправитель>// <Наименование>УТ 11.5</Наименование>// <Идентификатор>GUID_узла</Идентификатор>// </Отправитель>// <Данные>// <Объект ТипОбъекта="Справочник.Номенклатура">// ...поля объекта...// </Объект>// </Данные>// </Файл>// Чтение пакета EnterpriseDataЧтениеXML = Новый ЧтениеXML;ЧтениеXML.ОткрытьФайл(ПутьКФайлу);Десериализатор = Новый СериализаторXDTO(ФабрикаXDTO);Пока ЧтениеXML.Прочитать() ЦиклЕсли ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлементаИ ЧтениеXML.ЛокальноеИмя = "Объект" ТогдаОбъектXDTO = Десериализатор.ПрочитатьXML(ЧтениеXML);ОбработатьОбъектXDTO(ОбъектXDTO);КонецЕсли;КонецЦикла;
Правило 7: КД 2.0 — правила конвертации
КД 2.0 используется для обмена между разными конфигурациями с помощью файла правил конвертации (XML).
Паттерн загрузки через КД 2.0
// Загрузка данных с использованием обработки «Конвертация данных»Процедура ЗагрузитьДанныеПоПравилам(ПутьКФайлу, ПутьКПравилам) ЭкспортОбработкаЗагрузки = ВнешниеОбработки.Создать(ПутьКПравилам);ОбработкаЗагрузки.ИмяФайлаОбмена = ПутьКФайлу;ОбработкаЗагрузки.ПутьКПравиламОбмена = ПутьКПравилам;ОбработкаЗагрузки.ЗагрузитьПравилаОбмена();ПопыткаОбработкаЗагрузки.ВыполнитьЗагрузку();ИсключениеЗаписьЖурналаРегистрации(НСтр("ru = 'ОбменДанными.КД2.Загрузка'"),УровеньЖурналаРегистрации.Ошибка,,,ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));ВызватьИсключение;КонецПопытки;КонецПроцедуры
Отличия КД 2.0 от КД 3.0
| Аспект | КД 2.0 | КД 3.0 | |
|---|---|---|---|
| Правила | XML-файл | BSL-модули в конфигурации КД 3 | |
| Поддержка EDT | Нет | Да | |
| Формат | Proprietary XML | EnterpriseData (опционально) | |
| Обработчики | В правилах (строки кода) | Полноценный BSL, отладка | |
| Рекомендация | Существующие проекты | Новые проекты |
Правило 8: Диагностика и мониторинг обмена
Таблица событий журнала регистрации
При диагностике обмена искать события:
| Событие ЖР | Что означает | |
|---|---|---|
ОбменДанными | Общие события подсистемы БСП | |
ОбменДанными.Выгрузка | Формирование пакета | |
ОбменДанными.Загрузка | Приём и обработка пакета | |
ОбменДанными.Конфликт | Зафиксированные конфликты | |
ОбменДанными.Транспорт | Ошибки доставки |
Проверка состояния узлов обмена
// Диагностический запрос — состояние узлов и счётчиков сообщенийЗапрос = Новый Запрос;Запрос.Текст ="ВЫБРАТЬ| Узлы.Ссылка КАК Узел,| Узлы.Код,| Узлы.НомерОтправленного,| Узлы.НомерПринятого,| КОЛИЧЕСТВО(Изменения.Узел) КАК КоличествоИзменений|ИЗ| ПланОбмена.МойОбмен КАК Узлы| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ИзменениеПоПланамОбмена КАК Изменения| ПО Изменения.Узел = Узлы.Ссылка|ГДЕ| Узлы.Ссылка <> Узлы.ЭтотУзел|СГРУППИРОВАТЬ ПО| Узлы.Ссылка, Узлы.Код, Узлы.НомерОтправленного, Узлы.НомерПринятого";РезультатЗапроса = Запрос.Выполнить();
Ручная перерегистрация при сбоях
// Сброс счётчика и перерегистрация всех данных для узла// ВНИМАНИЕ: использовать только при полной ресинхронизацииПроцедура ПеререгистрироватьВсеДанные(УзелОбмена) Экспорт// Сбросить счётчикиУзелОбменаОбъект = УзелОбмена.ПолучитьОбъект();УзелОбменаОбъект.НомерОтправленного = 0;УзелОбменаОбъект.НомерПринятого = 0;УзелОбменаОбъект.Записать();// Удалить накопленные измененияПланыОбмена.УдалитьРегистрациюИзменений(УзелОбмена, 0);// Зарегистрировать заново// (зависит от состава плана обмена — перебрать все типы объектов)ЗаписьЖурналаРегистрации(НСтр("ru = 'ОбменДанными.Перерегистрация'"),УровеньЖурналаРегистрации.Предупреждение,Метаданные.ПланыОбмена.МойОбмен,УзелОбмена,НСтр("ru = 'Выполнена полная перерегистрация данных для узла.'"));КонецПроцедуры
Типичные ошибки
| Ошибка | Последствие | Как избежать | |
|---|---|---|---|
Нет проверки ОбменДанными.Загрузка | Бизнес-логика срабатывает при загрузке, данные портятся или зацикливается обмен | В каждом обработчике ПриЗаписи в первых строках | |
Фиксация НомерОтправленного до подтверждения доставки | Изменения помечены как отправленные, но до узла не дошли; при следующем сеансе не выгрузятся | Фиксировать номер только после подтверждения получателя | |
| Загрузка без проверки идемпотентности | Дублирование документов, двойные движения регистров | Проверять НомерСообщения <= НомерПринятого перед загрузкой | |
| Конфликт без логирования | Данные тихо перезаписаны, потеряны правки пользователя | Всегда писать в ЖР при конфликте с уровнем Предупреждение | |
| Регистрировать изменения внутри обработчика загрузки | Бесконечный обмен: А→Б→А→Б | Флаг Загрузка = Истина отключает регистрацию | |
| Длинная транзакция при загрузке большого пакета | Блокировки, таймауты, откат всего пакета | Загружать пообъектно с отдельными транзакциями или батчами | |
| Не удалять записи регистра изменений после выгрузки | Накопление миллионов записей, деградация производительности | ПланыОбмена.УдалитьРегистрациюИзменений после подтверждения |
Связанные ресурсы
- error-handling — канонический паттерн транзакций, обязательный при загрузке/выгрузке
- background-jobs — обмен данными часто выполняется в фоновых заданиях
depends_on:
- bsl-practices/error-handling
- bsl-practices/background-jobs