This commit is contained in:
artem 2025-10-13 20:49:29 +05:00
parent 05547fca58
commit 56f7f91160
10 changed files with 404 additions and 5 deletions

View File

@ -79,7 +79,7 @@
}, },
"Таблица 2 колонки": { "Таблица 2 колонки": {
"prefix": "таблица", "prefix": "таблица2",
"body": [ "body": [
"| Заголовок 1 | Заголовок 2 |", "| Заголовок 1 | Заголовок 2 |",
"|-------------|-------------|", "|-------------|-------------|",
@ -89,7 +89,7 @@
}, },
"Таблица 3 колонки": { "Таблица 3 колонки": {
"prefix": "таблица", "prefix": "таблица3",
"body": [ "body": [
"| Заголовок 1 | Заголовок 2 | Заголовок 3 |", "| Заголовок 1 | Заголовок 2 | Заголовок 3 |",
"|-------------|-------------|-------------|", "|-------------|-------------|-------------|",
@ -99,7 +99,7 @@
}, },
"Таблица 4 колонки": { "Таблица 4 колонки": {
"prefix": "таблица", "prefix": "таблица4",
"body": [ "body": [
"| Заголовок 1 | Заголовок 2 | Заголовок 3 | Заголовок 4 |", "| Заголовок 1 | Заголовок 2 | Заголовок 3 | Заголовок 4 |",
"|-------------|-------------|-------------|-------------|", "|-------------|-------------|-------------|-------------|",
@ -109,7 +109,7 @@
}, },
"Таблица 5 колонки": { "Таблица 5 колонки": {
"prefix": "таблица", "prefix": "таблица5",
"body": [ "body": [
"| Заголовок 1 | Заголовок 2 | Заголовок 3 | Заголовок 4 | Заголовок 5 |", "| Заголовок 1 | Заголовок 2 | Заголовок 3 | Заголовок 4 | Заголовок 5 |",
"|-------------|-------------|-------------|-------------|-------------|", "|-------------|-------------|-------------|-------------|-------------|",
@ -119,7 +119,7 @@
}, },
"Таблица 6 колонки": { "Таблица 6 колонки": {
"prefix": "таблица", "prefix": "таблица6",
"body": [ "body": [
"| Заголовок 1 | Заголовок 2 | Заголовок 3 | Заголовок 4 | Заголовок 5 | Заголовок 6 |", "| Заголовок 1 | Заголовок 2 | Заголовок 3 | Заголовок 4 | Заголовок 5 | Заголовок 6 |",
"|-------------|-------------|-------------|-------------|-------------|-------------|", "|-------------|-------------|-------------|-------------|-------------|-------------|",

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

View File

@ -0,0 +1,16 @@
---
title: Подключение и отключение базы к РИБ
slug: общие-обмен-данными-планы-обмена-распределенная-информационная-база/подключение-и-отключение-базы-к-риб
---
## Подключение базы
```bsl
УзелЦентральнойБазы = ПланыОбмена.Полный.НайтиПоКоду("ЦБ");
ПланыОбмена.УстановитьГлавныйУзел(УзелЦентральнойБазы);
```
## Отключение базы
```bsl
ПланыОбмена.УстановитьГлавныйУзел(Неопределено);
```
или запустить базу с аргументом `/ResetMasterNode`

View File

@ -0,0 +1,9 @@
---
title: Получить главный узел
slug: общие-обмен-данными-планы-обмена-распределенная-информационная-база/получить-главный-узел
---
```bsl
ГлавныйУзел = ПланыОбмена.ГлавныйУзел();
```

View File

@ -0,0 +1,178 @@
---
title: Реализация РИБ в базах без БСП
slug: общие-обмен-данными-планы-обмена-распределенная-информационная-база-реализация/реализация-риб-в-базах-без-бсп
---
# Перед началом работы
## Что это такое
Распределенная информационная база (РИБ) - это совокупность связанных информационных баз, в которых поддерживается синхронизация `конфигураций` и `данных`
РИБ служит для обмена идентичных информационных баз «1С: Предприятие 8». Чаще всего задача обменов между узлами РИБ сводится к выгрузке данных из **периферийных** узлов в **центральную базу**
В РИБ все конфигурации объеденены по строгой иерархии - центральная база может быть **только одна**, от которой создаются подчиненные узлы
![описание](/obmen-dannymi/rib/1.png)
## Особенности работы с РИБ
- Обмен данными выполняется в формате ХML документов. Изменения конфигурации распространяются в сообщениях обмена вместе с изменениями данных.
- Внесение изменений в конфигурацию возможно только в одном (корневом) узле распределённой системы. Изменения конфигурации передаются от главного узла к подчинённым
- Внесение изменений в данные возможно в любом узле системы. Изменения данных передаются между любыми связанными узлами.
- При выгрузке данных на всех объектах (справочники, документы и т. д.) невозможно внесение изменений (устанавливается блокировка). Это может парализовать рабочий процесс на длительное время. Чтобы избежать этого, рекомендуется включать обмен после работы либо выгружать небольшой объём информации за один раз.
- При обмене данными в случае, если объект был изменен в корневом и подчиненном узлах одновременно (коллизия), **ГЛАВНАЯ база перетирает изменения ПОДЧИНЕННОЙ**
# Пример работы в базах без БСП
## Создание подчиненных баз
1. Создаем план обмена. Во вкладке `Основное` устанавливаем флаги `Распределенная информационная база` и `Включать расширения конфигурации`
2. Настраиваем состав плана обмена. `Авторегистрацию` запрещаем, т.к. необходимо будет **вручную управлять регистрацией изменений**. (Мы же не хотим отправить документы дочернего узла 2 в дочерний узел 1 и наоборот)
![описание](/obmen-dannymi/rib/2.png)
3. Реализуем префиксацию объектов любым способом. Главное, чтобы коды и номера элементов в базах в РИБ не пересекались.
<details style="margin: 1em 0; padding: 0.5em; border: 1px solid #ccc; border-radius: 6px;">
<summary style="font-weight: bold; cursor: pointer;">Пример создания</summary>
Колхозное решение "на коленке"
- Создаем константу `ПрефиксИнформационнойБазы` (строка, 2 символа), **не включаем ее в план обмена**. Нам не нужно, чтобы она затиралась
- Создаем подписки на события:
| Имя подписки | Источник | Событие | Обработчик |
|-------------|-------------|-------------|-------------|
|`УстановитьПрефиксИнформационнойБазыНомеруДокумента`| все объекты `ДокументОбъект`, которые участвуют в обмене |`ПриУстановкеНовогоНомера`|`ПрефиксацияОбъектовСобытия.УстановитьПрефиксИнформационнойБазы` (реализуем ниже)|
|`УстановитьПрефиксИнформационнойБазыКодуСправочника`|все объекты `СправочникОбъект`, которые участвуют в обмене|`ПриУстановкеНовогоКода`|`ПрефиксацияОбъектовСобытия.УстановитьПрефиксИнформационнойБазы` (реализуем ниже)|
- В общем модуле реализуем метод префиксации:
```bsl
#Область ПрограммныйИнтерфейс
Процедура УстановитьПрефиксИнформационнойБазы(Источник, СтандартнаяОбработка, Префикс) Экспорт
ПрефиксИБ = Константы.ПрефиксИнформационнойБазы.Получить();
Если Не ЗначениеЗаполнено(ПрефиксИБ) Тогда
Префикс = "00-";
Иначе
Префикс = ПрефиксИБ + "-";
КонецЕсли;
КонецПроцедуры
#КонецОбласти
```
Все)
</details>
4. Создаем начальные образы подчиненных баз
В пользовательском режиме открываем созданный план обмена, настраиваем предопределенный узел текущей базы
Все созданные узлы в этом плане будут **подчинены** текущей базе.
![описание](/obmen-dannymi/rib/3.png)
Нажимаем команду `Создать начальный образ`, выбираем каталог, куда будет сохранен файл `.1cd` для файлового варианта или указываем параметры ИБ для серверного варианта
После этого просто создаем базу с теми параметрами подключения, которые указали выше (путь до папки в файловом варианте или строка подключения в серверном варианте)
В подчиненных базах для в пользовательском режиме можно будет посмотреть, какому узлу база подчинена
![описание](/obmen-dannymi/rib/4.png)
## Реализация выборочной регистрации изменений
Создаем подписки на событие:
| Имя подписки | Источник | Событие | Обработчик |
|-------------|-------------|-------------|-------------|
|`РегистрацияИзменений`| все **объекты**, которые участвуют в обмене |`ПриЗаписи`|`ОбменДаннымиРегистрацияСобытий.РИБ_РегистрацияПриЗаписи` (реализуем ниже)|
|`РегистрацияУдалений`| все **объекты**, которые участвуют в обмене |`ПередУдалением`|`ОбменДаннымиРегистрацияСобытий.РИБ_РегистрацияУдаленияПередУдалением` (реализуем ниже)|
Создаем общий модуль `ОбменДаннымиРегистрацияСобытий`, в котором определяем алгоритмы регистрации:
```bsl
#Область ПрограммныйИнтерфейс
Процедура РИБ_РегистрацияПриЗаписи(Источник, Отказ) Экспорт
ЗарегистрироватьИзменениеОбъекта(Источник, Отказ);
КонецПроцедуры
Процедура РИБ_РегистрацияУдаленияПередУдалением(Источник, Отказ) Экспорт
ЗарегистрироватьУдалениеОбъекта(Источник, Отказ);
КонецПроцедуры
#КонецОбласти
Процедура ЗарегистрироватьИзменениеОбъекта(Источник, Отказ)
Если Отказ Тогда
Возврат;
КонецЕсли;
ЭтоДокумент = Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(Источник.Ссылка));
// В примере подразумевается, что у всех документов есть реквизит "Магазин", по которому будем фильтровать подчиненные базы
Магазин = ?(ЭтоДокумент, Источник.Магазин, Неопределено);
// Получаем узлы, которые должны получить изменения выбранных документов
УзлыОбмена = ОпределитьПолучателейОбъекта(Магазин);
ПланыОбмена.ЗарегистрироватьИзменения(УзлыОбмена, Источник);
КонецПроцедуры
Процедура ЗарегистрироватьУдалениеОбъекта(Источник, Отказ)
Если Отказ Тогда
Возврат;
КонецЕсли;
ЭтоДокумент = Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(Источник.Ссылка));
Магазин = ?(ЭтоДокумент, Источник.Магазин, Неопределено);
УзлыОбмена = ОпределитьПолучателейОбъекта(Магазин);
ПланыОбмена.ЗарегистрироватьИзменения(УзлыОбмена, Новый УдалениеОбъекта(Источник.Ссылка));
КонецПроцедуры
Функция ОпределитьПолучателейОбъекта(Магазин = Неопределено)
УзлыОбмена = Новый Массив;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РИБ.Ссылка КАК Ссылка
|ИЗ
| ПланОбмена.РИБ КАК РИБ
|ГДЕ
| НЕ РИБ.ПометкаУдаления
| И НЕ РИБ.ЭтотУзел";
// "РИБ.Магазин = &Магазин" -
// для простоты примера принимается, что в плане обмена есть реквизит "Магазин", который заполняется для каждого узла
// На практике можно сделать соответствие "Узел-Магазин" любым другим способом, регистром сведений, например
Если Магазин <> Неопределено Тогда
Запрос.Текст = Запрос.Текст + "
|И РИБ.Магазин = &Магазин";
Запрос.УстановитьПараметр("Магазин", Магазин);
КонецЕсли;
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
УзлыОбмена.Добавить(Выборка.Ссылка);
КонецЦикла;
Возврат УзлыОбмена;
КонецФункции
#КонецОбласти
```

View File

@ -0,0 +1,19 @@
---
title: Создать начальный образ
slug: общие-обмен-данными-планы-обмена-распределенная-информационная-база/создать-начальный-образ
---
```bsl
// Создание нового узла обмена
НовыйУзел = ПланыОбмена.РИБ.СоздатьУзел();
НовыйУзел.Код = "М2";
НовыйУзел.Наименование = "Магазин № 2";
НовыйУзел.Записать();
// Создание начального образа для нового узла обмена
ПутьКБазеУзла = "File=""S:\Bases_1C\rib_bases\m2""";
ПланыОбмена.СоздатьНачальныйОбраз(НовыйУзел.Ссылка, ПутьКБазеУзла);
```

View File

@ -0,0 +1,177 @@
---
title: Чтение и запись сообщения обмена
slug: общие-обмен-данными-планы-обмена-распределенная-информационная-база/чтение-и-запись-сообщения-обмена
---
## Запись сообщения обмена
```bsl
УзелОтправитель = ПланыОбмена.РИБ.ЭтотУзел();
УзелПолучатель = ПланыОбмена.РИБ.НайтиПоКоду("М1");
ИмяФайлаСообщения = СтрШаблон("C:\temp\demo_exchange\Message_%1_%2.xml",
УзелОтправитель.Код, УзелПолучатель.Код);
// Открываем файл для записи
ЗаписьXML = Новый ЗаписьXML();
ЗаписьXML.ОткрытьФайл(ИмяФайлаСообщения);
// Создаем запись сообщения
ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();
ЗаписьСообщения.НачатьЗапись(ЗаписьXML, УзелПолучатель);
// Записываем изменения в файл
ПланыОбмена.ЗаписатьИзменения(ЗаписьСообщения);
ЗаписьСообщения.ЗакончитьЗапись();
// Закрываем файл
ЗаписьXML.Закрыть();
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Сообщение обмена успешно сформировано!";
Сообщение.Сообщить();
```
## Чтение сообщения обмена
### Вариант 1
```bsl
УзелОтправитель = ПланыОбмена.РИБ.НайтиПоКоду("М1");
УзелПолучатель = ПланыОбмена.РИБ.ЭтотУзел();
ИмяФайлаСообщения = СтрШаблон("C:\temp\demo_exchange\Message_%1_%2.xml",
УзелОтправитель.Код, УзелПолучатель.Код);
// Открываем файл для чтения
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ИмяФайлаСообщения);
// Создаем чтение сообщения
ЧтениеСообщения = ПланыОбмена.СоздатьЧтениеСообщения();
// Чтение сообщения обмена
Попытка
ЧтениеСообщения.НачатьЧтение(ЧтениеXML);
ПланыОбмена.ПрочитатьИзменения(ЧтениеСообщения);
Исключение
ТекстОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ТекстОшибки;
Сообщение.Сообщить();
КонецПопытки;
// Завершить чтение сообщения
ЧтениеСообщения.ЗакончитьЧтение();
ЧтениеXML.Закрыть();
```
### Вариант 2
```bsl
Функция ПрочитатьИПрименитьСообщениеОбмена(УзелОбмена, ИмяФайла) Экспорт
Результат = Новый Структура;
Результат.Вставить("Успешно", Ложь);
Результат.Вставить("ОписаниеОшибки", "");
Результат.Вставить("КоличествоОбъектов", 0);
Попытка
// Проверяем существование файла
Файл = Новый Файл(ИмяФайла);
Если НЕ Файл.Существует() Тогда
ВызватьИсключение "Файл сообщения не найден";
КонецЕсли;
// Открываем файл для чтения
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ИмяФайла);
// Создаем чтение сообщения
ЧтениеСообщения = ПланыОбмена.СоздатьЧтениеСообщения();
ЧтениеСообщения.НачатьЧтение(ЧтениеXML);
// Проверяем соответствие узлов
Если ЧтениеСообщения.Отправитель <> УзелОбмена Тогда
ВызватьИсключение СтрШаблон(
"Сообщение от другого узла. Ожидается: %1, Получено: %2",
УзелОбмена,
ЧтениеСообщения.Отправитель);
КонецЕсли;
// Проверяем номер сообщения
НомерПринятого = УзелОбмена.НомерПринятого;
НомерСообщения = ЧтениеСообщения.НомерСообщения;
Если НомерСообщения <= НомерПринятого Тогда
ВызватьИсключение СтрШаблон(
"Сообщение уже было принято. Номер сообщения: %1, Последний принятый: %2",
НомерСообщения,
НомерПринятого);
КонецЕсли;
// Начинаем транзакцию
НачатьТранзакцию();
Попытка
// Читаем и записываем объекты
Пока ЧтениеСообщения.Прочитать() Цикл
Данные = ЧтениеСообщения.Данные;
// Устанавливаем режим загрузки
Если ТипЗнч(Данные) <> Тип("УдалениеОбъекта") Тогда
Данные.ОбменДанными.Загрузка = Истина;
Данные.ОбменДанными.Отправитель = УзелОбмена;
Данные.Записать();
Иначе
// Удаление объекта
Попытка
УдаляемыйОбъект = Данные.Ссылка.ПолучитьОбъект();
Если УдаляемыйОбъект <> Неопределено Тогда
УдаляемыйОбъект.ОбменДанными.Загрузка = Истина;
УдаляемыйОбъект.Удалить();
КонецЕсли;
Исключение
// Объект уже удален
КонецПопытки;
КонецЕсли;
Результат.КоличествоОбъектов = Результат.КоличествоОбъектов + 1;
КонецЦикла;
// Обновляем номер принятого сообщения
УзелОбъектОбмена = УзелОбмена.ПолучитьОбъект();
УзелОбъектОбмена.НомерПринятого = НомерСообщения;
УзелОбъектОбмена.Записать();
// Фиксируем транзакцию
ЗафиксироватьТранзакцию();
Результат.Успешно = Истина;
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
// Закрываем чтение
ЧтениеСообщения.ЗакончитьЧтение();
ЧтениеXML.Закрыть();
Исключение
Результат.ОписаниеОшибки = ОписаниеОшибки();
// Записываем в журнал регистрации
ЗаписьЖурналаРегистрации("Обмен.ЧтениеСообщения",
УровеньЖурналаРегистрации.Ошибка,
,
УзелОбмена,
Результат.ОписаниеОшибки);
КонецПопытки;
Возврат Результат;
КонецФункции
```