---
title: Пример простого обмена двух баз через файл с квитированием
slug: общие-обмен-данными-планы-обмена/пример-простого-обмена-двух-баз-через-файл-с-квитированием
---

В общем виде обмен с квитированием осущетвляется в несколько шагов:
1. Создание сообщения обмена в источнике
2. Чтение и обработка сообщения обмена в приемнике
3. Создание подтверждающего сообщения в приемнике (квитирование)
4. Чтение и удаление успешно переданного из плана обмена в источнике


## Сообщение обмена Источник-Приемник

### Создание сообщения

Создаем план обмена, реализуем выгрузку. В примере в плане обмена источника зарегистрирован один справочник "Номенклатура"

```bsl
// Узел обмена - целевой узел обмена базы приемника в плане обмена
Процедура ВыгрузитьНоменклатуруВФайл(УзелОбмена) Экспорт
    
    Если Не ЗначениеЗаполнено(УзелОбмена) Тогда
        ВызватьИсключение "Не установлен узел обмена";
    КонецЕсли;
    
    // полное имя файла, куда будет записываться выгрузка
    ИмяСообщения = "C:\путь-до-папки-обмена\Выгрузка_Источник_Приемник.xml";
    
    ЗаписьXML = Новый ЗаписьXML;
    ЗаписьXML.ОткрытьФайл(ИмяСообщения, "UTF-8");
    
    ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();

    // тут записываются служебные заголовки сообщения - узел, номер сообщения и пр.
    ЗаписьСообщения.НачатьЗапись(ЗаписьXML, УзелОбмена);
    
    // выгружаем все изменения, дополнительно устанавливаем номер сообщения из ЗаписьСообщения,
    // т.к. до выгрузки изменений номер сообщения в таблице изменений = NULL
    ВыборкаИзменений = ПланыОбмена.ВыбратьИзменения(УзелОбмена, ЗаписьСообщения.НомерСообщения);
    
    // Записываем данные любым способом, которым удобно
    // тут приведен пример примитивной поузловой записи
    ЗаписьXML.ЗаписатьНачалоЭлемента("СписокНоменклатуры");
    
    Пока ВыборкаИзменений.Следующий() Цикл
        
        Данные = ВыборкаИзменений.Получить();
        
        ЗаписьXML.ЗаписатьНачалоЭлемента("Номенклатура");
            
            ЗаписьXML.ЗаписатьНачалоЭлемента("Код");
            ЗаписьXML.ЗаписатьТекст(Данные.Код);
            ЗаписьXML.ЗаписатьКонецЭлемента();
            
            ...
            
            ЗаписьXML.ЗаписатьНачалоЭлемента("ПометкаУдаления");
            ЗаписьXML.ЗаписатьТекст(Строка(Данные.ПометкаУдаления));
            ЗаписьXML.ЗаписатьКонецЭлемента();
            
        ЗаписьXML.ЗаписатьКонецЭлемента();
        
    КонецЦикла;
    
    ЗаписьXML.ЗаписатьКонецЭлемента();
    
    // закрываем служебные узлы сообщения
    ЗаписьСообщения.ЗакончитьЗапись();
    
    ЗаписьXML.Закрыть();
    
    Сообщение = Новый СообщениеПользователю;
    Сообщение.Текст = "Успешная выгрузка";
    Сообщение.Сообщить();
    
КонецПроцедуры

```

В результате получим xml вида:

```xml

<!--Начало блока, добавляемого методом ЗаписьСообщения.НачатьЗапись()-->
<v8msg:Message xmlns:v8msg="http://v8.1c.ru/messages">
    <v8msg:Header>
        <v8msg:ExchangePlan>СН_ОбменСПриемником</v8msg:ExchangePlan>
        <v8msg:To>ПБ</v8msg:To>
        <v8msg:From>ИБ</v8msg:From>
        <v8msg:MessageNo>16</v8msg:MessageNo>
        <v8msg:ReceivedNo>0</v8msg:ReceivedNo>
    </v8msg:Header>
    <v8msg:Body>
<!--конец блока, добавляемого методом ЗаписьСообщения.НачатьЗапись()-->

<!--Начало блока, который формируется нами вручную-->
        <СписокНоменклатуры>
            <Номенклатура>
                <Код>000000006</Код>
                ...
                <ПометкаУдаления>Нет</ПометкаУдаления>
            </Номенклатура>
            <Номенклатура>
                ...
            </Номенклатура>
            ...
            <Номенклатура>
                ...
            </Номенклатура>
        </СписокНоменклатуры>
<!--Конец блока, который формируется нами вручную-->

<!--Начало блока, добавляемого методом ЗаписьСообщения.ЗакончитьЗапись()-->
    </v8msg:Body>
</v8msg:Message>
<!--Конец блока, добавляемого методом ЗаписьСообщения.ЗакончитьЗапись()-->

```

### Чтение сообщения в базе-приемнике

> **ВАЖНО! В приемнике должен быть план обмена с таким же именем, что и в источнике. Также нужно, чтобы код узла базы приемника совпадал с кодом, указанным в шапке сообщения в блоке `<v8msg:To>`** . Это необходимо для того, чтобы методы чтения сообщения корректно сработали

Считываем данные для обработки в приемнике

```bsl
// ПолноеИмяФайла - путь к файлу обмена, созданному источником
// ТаблицаДляЗагрузки - промежуточная таблица для последующей загрузки в запрос
Процедура ЗаполнитьТаблицуДляЗагрузки(ПолноеИмяФайла, ТаблицаДляЗагрузки)
    
    СоответствиеКолонокУзлам = Новый Соответствие;
    СоответствиеКолонокУзлам.Вставить("Наименование", "Наименование");
    ...
    СоответствиеКолонокУзлам.Вставить("ПометкаУдаления", "ПометкаУдаления");
    
    // Создаем объект чтения сообщения
    ЧтениеСообщения = ПланыОбмена.СоздатьЧтениеСообщения();
    
    // Создаем объект, который будет использоваться чтением сообщения
    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.ОткрытьФайл(ПолноеИмяФайла);
    
    // Считываем служебную информацию сообщения. Код узла, номера сообщений и пр.
    ЧтениеСообщения.НачатьЧтение(ЧтениеXML);

    // Проверяем, что отправитель это именно та база, откуда мы ожидаем сообщение
    Если ЧтениеСообщения.Отправитель <> "ИБ" Тогда
        //Обработка ошибки
        Возврат;
    КонецЕсли;

    // Считываем данные, записанные в <body> узле сообщения
    Пока ЧтениеXML.Прочитать() Цикл
        
        // Мы должны закончить чтение на узле </v8msg:Body>,
        // иначе метод ЧтениеСообщения.ЗакончитьЧтение() завершится с ошибкой
        Если ЧтениеXML.ТипУзла = ТипУзлаXML.КонецЭлемента
            И ЧтениеXML.Имя = "v8msg:Body" Тогда
            Прервать;		
        КонецЕсли; 

        Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
            
            ИмяТекущегоУзла = ЧтениеXML.Имя;
            Если ИмяТекущегоУзла = "Номенклатура" Тогда
                СтрокаТаблицы = ТаблицаДляЗагрузки.Добавить();
            КонецЕсли;	
            
        ИначеЕсли ЧтениеXML.ТипУзла = ТипУзлаXML.Текст Тогда
            
            ИмяКолонкиТаблицы = СоответствиеКолонокУзлам.Получить(ИмяТекущегоУзла);
            
            Если ИмяКолонкиТаблицы <> Неопределено Тогда
                СтрокаТаблицы[ИмяКолонкиТаблицы] = ЧтениеXML.Значение;
            КонецЕсли;
            
        КонецЕсли;
        
    КонецЦикла;
    
    // Считываем закрывающие служебные тэги сообщения
    ЧтениеСообщения.ЗакончитьЧтение();
    
    // завершаем чтение файла
    ЧтениеXML.Закрыть();
    
КонецПроцедуры

```
Чтение будет проходить следующими этапами

```xml

<!--ЧтениеСообщения.НачатьЧтение()-->
<v8msg:Message xmlns:v8msg="http://v8.1c.ru/messages">
    <v8msg:Header>
        <v8msg:ExchangePlan>СН_ОбменСПриемником</v8msg:ExchangePlan>
        <v8msg:To>ПБ</v8msg:To>
        <v8msg:From>ИБ</v8msg:From>
        <v8msg:MessageNo>16</v8msg:MessageNo>
        <v8msg:ReceivedNo>0</v8msg:ReceivedNo>
    </v8msg:Header>
    <v8msg:Body>
<!--Конец ЧтениеСообщения.НачатьЧтение()-->

<!--ЧтениеXML.Прочитать()-->
        <СписокНоменклатуры>
            <Номенклатура>
                <Код>000000006</Код>
                ...
                <ПометкаУдаления>Нет</ПометкаУдаления>
            </Номенклатура>
            <Номенклатура>
                ...
            </Номенклатура>
            <Номенклатура>
                ...
            </Номенклатура>
            ...
            <Номенклатура>
                ...
            </Номенклатура>
        </СписокНоменклатуры>
<!--Конец ЧтениеXML.Прочитать()-->

<!--ЧтениеСообщения.ЗакончитьЧтение()-->
    </v8msg:Body>
</v8msg:Message>
<!--Конец ЧтениеСообщения.ЗакончитьЧтение()-->

<!--ЧтениеXML.Закрыть()-->

```

Дальше обрабатываем данные файла в приемнике как нужно.

## Сообщение Приемник-Источник. Квитирование (гарантия доставки)

### Формирование сообщения в базе приемнике

Квитирование необходимо для того, чтобы дать источнику данные о том, какие сообщения приемник получил и успешно обработал. Записи таблицы изменений, где номер сообщения меньше либо равен номеру, указанному в квитанции можно удалять из таблицы изменений.

Простыми словами, после того, как в базе источнике мы обработали все данные, нужно сформировать ответное сообщение только с шапкой, без данных. В шапке будет указан `НомерПринятого`, который будет равен `НомеруОтправленного` из полученного ранее сообщения

```bsl

Процедура СформироватьКвитанциюЗаписатьВКаталогОбмена()
    
    // куда сохраняем квитанцию
    ИмяФайла = "C:\путь-до-папки-обмена\Квитанция_ПБ_ИБ.xml";
    
    // Узел в плане обмена, которому направляем квитанцию о получении
    Узел = ПланыОбмена.СН_ОбменСПриемником.НайтиПоКоду("ИБ");
    Если Не ЗначениеЗаполнено(Узел) Тогда
        ВызватьИсключение "Неопределенный узел получателя квитанции";
    КонецЕсли;
    
    Запись = Новый ЗаписьXML;
    Запись.ОткрытьФайл(ИмяФайла);
    
    // Тут записываем шапку
    ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();
    ЗаписьСообщения.НачатьЗапись(Запись, Узел);
    
    // заканчиваем запись, не записывая тело. Только шапка
    ЗаписьСообщения.ЗакончитьЗапись();
    
    // Закрываем файл
    Запись.Закрыть();
    
КонецПроцедуры

```

Пример квитанции
```xml
<v8msg:Message xmlns:v8msg="http://v8.1c.ru/messages">
    <v8msg:Header>
        <v8msg:ExchangePlan>СН_ОбменСПриемником</v8msg:ExchangePlan>
        <v8msg:To>ИБ</v8msg:To>
        <v8msg:From>ПБ</v8msg:From>
        <v8msg:MessageNo>4</v8msg:MessageNo>
        <v8msg:ReceivedNo>20</v8msg:ReceivedNo>
    </v8msg:Header>
    <v8msg:Body/>
</v8msg:Message>
```

### Чтение квитанции в базе-источнике. Удаление записей о регистрации изменений
В источнике нужно считать квитанцию, сформированную выше, и удалить из таблиц изменений все записи, **номер отправленного** которых меньше или равен **номеру принятого в квитанции**

```bsl
Процедура ПрочитатьКвитанциюУдалитьИзмененияИзПланаОбмена(УзелОбмена) Экспорт
	
	ПутьКФайлу = "C:\путь-до-папки-обмена\Квитанция_ПБ_ИБ.xml";
	
	ЧтениеСообщения = ПланыОбмена.СоздатьЧтениеСообщения();
	
	Чтение = Новый ЧтениеXML;
	Чтение.ОткрытьФайл(ПутьКФайлу);
	
    // считываем квитанцию
	ЧтениеСообщения.НачатьЧтение(Чтение);
	
	НомерПринятого = ЧтениеСообщения.НомерПринятого;
	
    // удаляем переданное по номеру принятого, указанного в квитанции
	ПланыОбмена.УдалитьРегистрациюИзменений(УзелОбмена, НомерПринятого);
	
	ЧтениеСообщения.ЗакончитьЧтение();
	
	Чтение.Закрыть();
	
    // удаляем квитанции
	УдалитьФайлы(ПутьКФайлу);
	
КонецПроцедуры
```