ЗаписьСообщенияОбмена.ПрерватьЗапись

Методы · CancelWrite

Прерывает запись сообщения. Сообщение при этом считается не отправленным. Запись сообщения обмена может быть завершена либо обращением к методу ЗакончитьЗапись, либо обращением к методу ПрерватьЗапись. Если запись была начата и не была завершена тем или иным способом перед удалением объекта ЗаписьСообщенияОбмена, то перед удалением производится неявный вызов метода ПрерватьЗапись. Аналогично, если не была завершена запись сообщения и было обращение к методу НачатьЗапись, то запись предыдущего сообщения прерывается.

Синтаксис

ПрерватьЗапись()

Примеры платформы

ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ИмяФайла);
ЗаписьСообщения.НачатьЗапись(ЗаписьXML, Получатель);
Если Неудача Тогда
    ЗаписьСообщения.ПрерватьЗапись();
Иначе
    ЗаписьСообщения.ЗакончитьЗапись();
КонецЕсли;
Полное описание, параметры и ограничения

ЗаписьСообщенияОбмена.ПрерватьЗапись (ExchangeMessageWriter.CancelWrite)

ЗаписьСообщенияОбмена (ExchangeMessageWriter)

ПрерватьЗапись (CancelWrite)

Доступен, начиная с версии 8.0.

Синтаксис:
ПрерватьЗапись()
Описание:

Прерывает запись сообщения. Сообщение при этом считается не отправленным. Запись сообщения обмена может быть завершена либо обращением к методу ЗакончитьЗапись, либо обращением к методу ПрерватьЗапись.
Если запись была начата и не была завершена тем или иным способом перед удалением объекта ЗаписьСообщенияОбмена, то перед удалением производится неявный вызов метода ПрерватьЗапись. Аналогично, если не была завершена запись сообщения и было обращение к методу НачатьЗапись, то запись предыдущего сообщения прерывается.

Доступность:

Сервер, толстый клиент, внешнее соединение, мобильное приложение (сервер), мобильный автономный сервер.

Пример:

ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ИмяФайла);
ЗаписьСообщения.НачатьЗапись(ЗаписьXML, Получатель);
// Запись тела сообщения

Если Неудача Тогда
ЗаписьСообщения.ПрерватьЗапись();
Иначе
ЗаписьСообщения.ЗакончитьЗапись();
КонецЕсли;

Использование в версии:

Доступен, начиная с версии 8.0.

Методическая информация

Практические примеры

Своя двухузловая РИБ: регистрация → пакет → actual чтение → ответ → безопасная очистка

Отдельная учебная РИБ A/B с одной собственной строкой и только собственными ссылками. Полный XML-протокол использует настоящие factory writer/reader, нативный начальный образ B и различает регистрацию, отправку, импорт и реально принятый ответ. Код только authored/static, не выполнялся в 1С.

Где и как запускать

Полный ordinary thick модуль собственной формы УчебнаяРИБ85, exact platform8.5.1.1343. Manual два файловых own A/B по указанным UUID путям; actual B только native initial imageA. Никаких actual действий агентом.

Подготовка

  1. НИЧЕГО из этого урока не выполняйте в рабочей базе. Вручную создайте новый каталог C:\1C85_OwnExchangeLab\c88b7ab1-1d4c-4893-b6be-1f16f4b85293 и отдельную совершенно новую пустую файловую базу A внутри него. Каталог B должен отсутствовать полностью. Если UUID-папка уже содержит чужие данные или B существует — выберите другой учебный стенд и адаптируйте все связанные guards вместе; не удаляйте/не переиспользуйте содержимое.

  2. В A, только платформа8.5.1.1343, Имя конфигурации=УчебнаяРИБ85; основное приложение Обычное. Ровно один неиерархический каталог МояСтрока85: Код Строка8, уникальность включена, автоматическая нумерация выключена, Наименование Строка40; собственные реквизиты Количество85 Число(9,0), Метка85 Строка36. Нет иных каталогов, документов, регистров, подписок, фоновых/регламентных заданий, обменов, расширений и рабочих modules.

  3. Ровно один ПланОбмена МояРИБ85: РаспределеннаяИнформационнаяБаза=Да, Код Строка1, Наименование Строка40. В составе только каталог МояСтрока85, его АвтоРегистрация=Запретить. Не включайте узлы плана, константы или другие объекты в состав. После обновления только собственного A откройте его в обычном клиенте: Операции → Планы обмена → МояРИБ85. У своего предопределённого ЭтотУзел задайте Код A, Наименование Мой A c88b7ab1. В том же списке создайте второй actual узел Код B, Наименование Мой B c88b7ab1. Иные узлы/пометки удаления отсутствуют. Не присваивайте выдуманные UUID узлам.

  4. Создайте обработку УчебнаяРИБ85 и Обычную Основную форму. Реквизиты: Исходящее85, Входящее85 и Состояние85 Строка неограниченной длины. Покажите первые два как большие многострочные поля: Исходящее85 только просмотр, Входящее85 принимает лишь XML, вручную скопированный из своей другой формы. Состояние85 только просмотр. Код целиком в модуле этой ordinary формы; ПриОткрытии() назначьте одноимённому событию.

  5. Добавьте кнопки с одноимёнными действиями: СоздатьТолькоСвойНачальныйB85, ЗарегистрироватьТолькоСвоюСтроку85, ПрерватьСвойWriter85, ОтправитьСвоюСтрокуA85, ПосмотретьИПрерватьСвойReader85, ПринятьСвойПакет85, ОтветитьТолькоИзСвоегоB85, ОчиститьТолькоПодтвержденнуюСвоюСтроку85, ОтсоединитьИВосстановитьТолькоСвойChildB85. Обновите только свой A; открывайте thick ordinary клиентом.

  6. Пока каталог МояСтрока85 пуст: A→СоздатьТолькоСвойНачальныйB85. Это настоящий native СоздатьНачальныйОбраз для своего actual nodeB и только несуществующего собственного UUID-dir B. Метод создаёт базу и удаляет регистрации элементов данных/конфигурации данного узла — не запускайте после рабочих регистраций. Агент НЕ выполнял это создание, файловые вызовы или копирование базы.

  7. Зарегистрируйте B в своём launcher по ровно C:\1C85_OwnExchangeLab\c88b7ab1-1d4c-4893-b6be-1f16f4b85293\B, без серверного соединения. A/B пути укажите точно, без завершающего слеша, относительных путей и дополнительных частей File: guard требует точное буквальное имя вместе с Файл.ПолноеИмя; он не обещает разрешать symlinks или альтернативные написания. Откройте тот же код формы из реального образа B: native ЭтотУзел обязан стать B, native ГлавныйУзел=A. Не создавайте независимую B вручную — её actual ссылки/metadata происходят только из начального образа. Сначала обе базы имеют пустой каталог, sent/received counters0.

  8. После создания B вручную только в A через Операции → Справочники → МояСтрока85 создайте единственную свою запись: Код Э85, Наименование Мой обмен85, Количество85=7, Метка85=c88b7ab1-1d4c-4893-b6be-1f16f4b85293. В B она появится только из real импорта. Авторегистрация запрещена, поэтому явную регистрацию выполняет own команда. До завершения упражнения строку, CF, extension, nodes, counters не редактируйте; A-форма остаётся открытой до собственного ответа B.

  9. Два собственных клиента, по одному сеансу на каждую базу; никаких других пользователей, фоновых задач или параллельного обмена. Не вставляйте внешние/чужие XML. Сообщения передаются вручную между строками своих двух форм, не через сеть/файлы/production endpoints.

#Если ТолстыйКлиентОбычноеПриложение Тогда

Перем МояСторона85, МойЭтотУзел85, МойДругойУзел85, МояСсылка85, МойНомерОтправки85, МояВерсияОтправки85, МойПодтвержденныйНомер85;

Процедура ПриОткрытии()
    ПроверитьСвойПустойСтенд();
    ОпределитьСвоиУзлы85();
    МойНомерОтправки85 = 0;
    МояВерсияОтправки85 = "";
    МойПодтвержденныйНомер85 = 0;
    Исходящее85 = "";
    Входящее85 = "";
    Состояние85 = "Собственный узел " + МояСторона85 + ". Обмен только между собственными A/B; сообщения в строках формы.";
КонецПроцедуры

Процедура ОпределитьСвоиУзлы85()
    План = Метаданные.ПланыОбмена.Найти("МояРИБ85");
    Каталог = Метаданные.Справочники.Найти("МояСтрока85");
    Если План = Неопределено Или Каталог = Неопределено Тогда
        ВызватьИсключение "Нужны только собственные МояРИБ85 и МояСтрока85";
    КонецЕсли;
    Если Не План.РаспределеннаяИнформационнаяБаза Или План.Состав.Количество() <> 1 Или Не План.Состав.Содержит(Каталог) Тогда
        ВызватьИсключение "Нужна настоящая РИБ, в составе только собственный каталог";
    КонецЕсли;
    Если План.Состав.Найти(Каталог).АвтоРегистрация <> АвтоРегистрацияИзменений.Запретить Тогда
        ВызватьИсключение "Свой каталог должен иметь запрещённую авторегистрацию для явного учебного контроля";
    КонецЕсли;
    Проверка = Новый Запрос("ВЫБРАТЬ Ссылка, Код, Наименование, ПометкаУдаления ИЗ ПланОбмена.МояРИБ85");
    Узлы = Проверка.Выполнить().Выгрузить();
    Если Узлы.Количество() <> 2 Тогда
        ВызватьИсключение "Нужны ровно два реально подготовленных собственных узла A/B";
    КонецЕсли;
    А = Узлы.Найти("A", "Код");
    Б = Узлы.Найти("B", "Код");
    Если А = Неопределено Или Б = Неопределено Тогда
        ВызватьИсключение "Нет точных собственных узлов A/B";
    КонецЕсли;
    Если А.Наименование <> "Мой A c88b7ab1" Или Б.Наименование <> "Мой B c88b7ab1" Или А.ПометкаУдаления Или Б.ПометкаУдаления Тогда
        ВызватьИсключение "Имена или состояние собственных узлов не совпали";
    КонецЕсли;
    МойЭтотУзел85 = ПланыОбмена.МояРИБ85.ЭтотУзел();
    Если МойЭтотУзел85 = А.Ссылка Тогда
        МояСторона85 = "A";
        МойДругойУзел85 = Б.Ссылка;
    ИначеЕсли МойЭтотУзел85 = Б.Ссылка Тогда
        МояСторона85 = "B";
        МойДругойУзел85 = А.Ссылка;
    Иначе
        ВызватьИсключение "ЭтотУзел не совпал с одной из двух actual собственных ссылок";
    КонецЕсли;
    Если МойДругойУзел85 = МойЭтотУзел85 Или Не ЗначениеЗаполнено(МойДругойУзел85) Тогда
        ВызватьИсключение "Получатель должен быть реальным другим собственным узлом";
    КонецЕсли;
    ПроверитьСвойПуть85(МояСторона85);
    Главный = ПланыОбмена.ГлавныйУзел();
    Если (МояСторона85 = "A" И Главный <> Неопределено) Или (МояСторона85 = "B" И Главный <> МойДругойУзел85) Тогда
        ВызватьИсключение "A должен быть actual корнем, B настоящим дочерним образом A";
    КонецЕсли;
КонецПроцедуры

Процедура ПроверитьСвойПуть85(Сторона)
    Части = СтрРазделить(СтрокаСоединенияИнформационнойБазы(), ";");
    Если Части.Количество() <> 1 И Части.Количество() <> 2 Тогда
        ВызватьИсключение "Разрешён только единственный File без дополнительных параметров";
    КонецЕсли;
    Если Части.Количество() = 2 И СокрЛП(Части[1]) <> "" Тогда
        ВызватьИсключение "После собственного File допускается лишь пустой завершающий разделитель";
    КонецЕсли;
    Параметр = СокрЛП(Части[0]);
    Если НРег(Лев(Параметр, 5)) <> "file=" Тогда
        ВызватьИсключение "Серверная или чужая строка соединения запрещена";
    КонецЕсли;
    Путь = Сред(Параметр, 6);
    Если Лев(Путь, 1) = """" И Прав(Путь, 1) = """" Тогда
        Путь = Сред(Путь, 2, СтрДлина(Путь) - 2);
    КонецЕсли;
    ФайлБазы = Новый Файл(Путь);
    Ожидание = "C:\1C85_OwnExchangeLab\c88b7ab1-1d4c-4893-b6be-1f16f4b85293\" + Сторона;
    Если НРег(Путь) <> НРег(Ожидание) Или НРег(ФайлБазы.ПолноеИмя) <> НРег(Ожидание) Тогда
        ВызватьИсключение "Разрешены только точные собственные UUID-каталоги A/B";
    КонецЕсли;
КонецПроцедуры

Процедура ПроверитьСвоюСторону85(Сторона)
    ПроверитьСвойПустойСтенд();
    ОпределитьСвоиУзлы85();
    Если МояСторона85 <> Сторона Тогда
        ВызватьИсключение "Команда предназначена только своей стороне " + Сторона;
    КонецЕсли;
КонецПроцедуры

Процедура СоздатьТолькоСвойНачальныйB85(Кнопка)
    ПроверитьСвоюСторону85("A");
    Каталог = Новый Запрос("ВЫБРАТЬ Ссылка ИЗ Справочник.МояСтрока85");
    Если Каталог.Выполнить().Выгрузить().Количество() <> 0 Тогда
        ВызватьИсключение "Начальный образ создаётся до единственной своей строки, каталог обязан быть пустым";
    КонецЕсли;
    Цель = "C:\1C85_OwnExchangeLab\c88b7ab1-1d4c-4893-b6be-1f16f4b85293\B";
    ФайлЦели = Новый Файл(Цель);
    Если НРег(ФайлЦели.ПолноеИмя) <> НРег(Цель) Или ФайлЦели.Существует() Тогда
        ВызватьИсключение "Только точный собственный ещё не существующий B; ничего не заменяем и не очищаем";
    КонецЕсли;
    Если МойДругойУзел85.Код <> "B" Или МойДругойУзел85.ЭтотУзел Или ПланыОбмена.ГлавныйУзел() <> Неопределено Тогда
        ВызватьИсключение "Изображение разрешено только своему childB из настоящего корня A";
    КонецЕсли;
    ПланыОбмена.СоздатьНачальныйОбраз(МойДругойУзел85, "File=""" + Цель + """");
    Если Не ФайлЦели.Существует() Тогда
        ВызватьИсключение "Native создание образа не подтверждено существованием собственного B; дальнейший обмен запрещён";
    КонецЕсли;
    Состояние85 = "Создан только собственный начальный B. Native метод удаляет регистрации этого узла при создании образа; это не резервная копия. Откройте именно B, затем вручную подготовьте одну строку в A.";
КонецПроцедуры

Функция ПрочитатьСвоюСтроку85()
    Запрос = Новый Запрос("ВЫБРАТЬ Ссылка, Код, Наименование, Количество85, Метка85, ПометкаУдаления ИЗ Справочник.МояСтрока85");
    Строки = Запрос.Выполнить().Выгрузить();
    Если Строки.Количество() <> 1 Тогда
        ВызватьИсключение "Разрешена ровно одна actual собственная строка";
    КонецЕсли;
    СтрокаДанных = Строки[0];
    Если СтрокаДанных.Код <> "Э85" Или СтрокаДанных.Наименование <> "Мой обмен85" Или СтрокаДанных.Количество85 <> 7
        Или СтрокаДанных.Метка85 <> "c88b7ab1-1d4c-4893-b6be-1f16f4b85293" Или СтрокаДанных.ПометкаУдаления Тогда
        ВызватьИсключение "Own marker/code/name/value не совпали; чужие или изменённые строки запрещены";
    КонецЕсли;
    Возврат СтрокаДанных.Ссылка;
КонецФункции

Процедура ЗарегистрироватьТолькоСвоюСтроку85(Кнопка)
    ПроверитьСвоюСторону85("A");
    МояСсылка85 = ПрочитатьСвоюСтроку85();
    Если ПланыОбмена.ИзменениеЗарегистрировано(МойДругойУзел85, МояСсылка85) Тогда
        ВызватьИсключение "Для отдельного опыта нужна ещё не зарегистрированная own строка; ничего не сбрасываем автоматически";
    КонецЕсли;
    ПланыОбмена.ЗарегистрироватьИзменения(МойДругойУзел85, МояСсылка85);
    Если Не ПланыОбмена.ИзменениеЗарегистрировано(МойДругойУзел85, МояСсылка85) Тогда
        ВызватьИсключение "Actual регистрация своей строки не подтверждена readback";
    КонецЕсли;
    Состояние85 = "Зарегистрирована только одна own ссылка для своего B. Это ещё не отправка, не импорт и не ack.";
КонецПроцедуры

Процедура ПрерватьСвойWriter85(Кнопка)
    ПроверитьСвойПустойСтенд();
    ОпределитьСвоиУзлы85();
    Было = МойДругойУзел85.ПолучитьОбъект().НомерОтправленного;
    Поток = Новый ЗаписьXML;
    Поток.УстановитьСтроку();
    Writer = ПланыОбмена.СоздатьЗаписьСообщения();
    Попытка
        Writer.НачатьЗапись(Поток, МойДругойУзел85);
        ПроверитьСвойАктивныйWriter85(Writer, Поток);
        Если Writer.НомерСообщения <> Было + 1 Тогда
            ВызватьИсключение "Начатый own writer должен предложить следующий номер";
        КонецЕсли;
        Writer.ПрерватьЗапись();
    Исключение
        ПрерватьИЗакрытьСвойWriterПослеОшибки85(Writer, Поток, ОписаниеОшибки());
    КонецПопытки;
    Поток.Закрыть();
    ПроверитьСвойНеактивныйWriter85(Writer);
    Если МойДругойУзел85.ПолучитьОбъект().НомерОтправленного <> Было Тогда
        ВызватьИсключение "Прерванный writer не должен стать отправленным";
    КонецЕсли;
    Состояние85 = "Own writer прерван до выборки и записи тела. Номер отправленного не изменился; регистрационные номера выборки здесь вообще не менялись.";
КонецПроцедуры

Процедура ОтправитьСвоюСтрокуA85(Кнопка)
    ПроверитьСвоюСторону85("A");
    МояСсылка85 = ПрочитатьСвоюСтроку85();
    Если МойНомерОтправки85 <> 0 Или Не ПланыОбмена.ИзменениеЗарегистрировано(МойДругойУзел85, МояСсылка85) Тогда
        ВызватьИсключение "Сначала одна own регистрация; эта форма отправляет её один раз";
    КонецЕсли;
    МояВерсияОтправки85 = МояСсылка85.ПолучитьОбъект().ВерсияДанных;
    Результат = ЗаписатьСвойПакет85(Истина);
    МойНомерОтправки85 = Результат.Номер;
    Исходящее85 = Результат.XML;
    Если Не ПланыОбмена.ИзменениеЗарегистрировано(МойДругойУзел85, МояСсылка85) Тогда
        ВызватьИсключение "Одна отправка не является подтверждённым приёмом, own регистрацию нельзя считать ack";
    КонецЕсли;
    Состояние85 = "Native пакет A номер " + Строка(МойНомерОтправки85) + " сформирован. Скопируйте только этот own XML в B.Входящее85; форма A должна оставаться открытой до её actual ответа.";
КонецПроцедуры

Функция ЗаписатьСвойПакет85(ВыбратьОднуСтроку)
    Было = МойДругойУзел85.ПолучитьОбъект().НомерОтправленного;
    Поток = Новый ЗаписьXML;
    Поток.УстановитьСтроку();
    Writer = ПланыОбмена.СоздатьЗаписьСообщения();
    Попытка
        Если ТипЗнч(Writer) <> Тип("ЗаписьСообщенияОбмена") Тогда
            ВызватьИсключение "Native factory должен вернуть actual writer";
        КонецЕсли;
        Writer.НачатьЗапись(Поток, МойДругойУзел85);
        ПроверитьСвойАктивныйWriter85(Writer, Поток);
        Номер = Writer.НомерСообщения;
        Ack = Writer.НомерПринятого;
        Если ВыбратьОднуСтроку Тогда
            Выборка = ПланыОбмена.ВыбратьИзменения(Writer.Получатель, Номер, МояСсылка85);
            Количество = 0;
            Пока Выборка.Следующий() Цикл
                Объект = Выборка.Получить();
                Если ТипЗнч(Объект) <> Тип("СправочникОбъект.МояСтрока85") Тогда
                    ВызватьИсключение "Own фильтр должен выбрать только actual объект своей строки, не удаление или другую metadata";
                КонецЕсли;
                Если Объект.Ссылка <> МояСсылка85 Или Объект.Метка85 <> "c88b7ab1-1d4c-4893-b6be-1f16f4b85293" Тогда
                    ВызватьИсключение "Actual выборка не соответствует own ссылке/маркеру";
                КонецЕсли;
                Количество = Количество + 1;
            КонецЦикла;
            Если Количество <> 1 Тогда
                ВызватьИсключение "Должно быть ровно одно actual изменение своей строки";
            КонецЕсли;
        КонецЕсли;
        ПланыОбмена.ЗаписатьИзменения(Writer, 0);
        Writer.ЗакончитьЗапись();
    Исключение
        ПрерватьИЗакрытьСвойWriterПослеОшибки85(Writer, Поток, ОписаниеОшибки());
    КонецПопытки;
    XML = Поток.Закрыть();
    ПроверитьСвойНеактивныйWriter85(Writer);
    Если МойДругойУзел85.ПолучитьОбъект().НомерОтправленного <> Номер Или Номер <> Было + 1 Или СтрДлина(XML) = 0 Тогда
        ВызватьИсключение "Own законченный writer/counter/XML не совпали; не объявляем доставку";
    КонецЕсли;
    Возврат Новый Структура("XML,Номер,Ack", XML, Номер, Ack);
КонецФункции

Процедура ПрерватьИЗакрытьСвойWriterПослеОшибки85(Writer, Поток, ИсходнаяОшибка)
    ОшибкиОсвобождения = "";
    Попытка
        Если Writer.ЗаписьXML <> Неопределено Тогда
            Writer.ПрерватьЗапись();
        КонецЕсли;
    Исключение
        ОшибкиОсвобождения = "; прервать writer не удалось: " + ОписаниеОшибки();
    КонецПопытки;
    Попытка
        Поток.Закрыть();
    Исключение
        ОшибкиОсвобождения = ОшибкиОсвобождения + "; закрыть XML writer не удалось: " + ОписаниеОшибки();
    КонецПопытки;
    ВызватьИсключение ИсходнаяОшибка + ОшибкиОсвобождения;
КонецПроцедуры

Процедура ПроверитьСвойАктивныйWriter85(Writer, Поток)
    Если Writer.ЗаписьXML <> Поток Или Writer.Получатель <> МойДругойУзел85 Или Writer.Получатель = МойЭтотУзел85
        Или Writer.НомерСообщения <= 0 Или Writer.НомерПринятого <> МойДругойУзел85.ПолучитьОбъект().НомерПринятого Тогда
        ВызватьИсключение "Actual writer XML/recipient/number/own received ack не совпали";
    КонецЕсли;
КонецПроцедуры

Процедура ПроверитьСвойНеактивныйWriter85(Writer)
    Если Writer.ЗаписьXML <> Неопределено Или Writer.Получатель <> Неопределено
        Или Writer.НомерСообщения <> Неопределено Или Writer.НомерПринятого <> 0 Тогда
        ВызватьИсключение "После Finish/Abort native writer должен сбросить документированные временные свойства";
    КонецЕсли;
КонецПроцедуры

Процедура ПосмотретьИПрерватьСвойReader85(Кнопка)
    ПроверитьСвойПустойСтенд();
    ОпределитьСвоиУзлы85();
    ПроверитьОписаниеСвоегоПакета85(Входящее85);
    Состояние85 = "Отдельный native reader прочитал описание и был явно прерван. Узел не принял сообщение; для импорта нужен НОВЫЙ reader на том же own XML.";
КонецПроцедуры

Процедура ПроверитьОписаниеСвоегоПакета85(XML)
    Если СтрДлина(XML) = 0 Или СтрДлина(XML) > 2000000 Тогда
        ВызватьИсключение "Разрешён только небольшой XML, скопированный из своей второй формы";
    КонецЕсли;
    Было = МойДругойУзел85.ПолучитьОбъект().НомерПринятого;
    Поток = Новый ЧтениеXML;
    Поток.УстановитьСтроку(XML);
    Reader = ПланыОбмена.СоздатьЧтениеСообщения();
    Попытка
        Reader.НачатьЧтение(Поток, ДопустимыйНомерСообщения.Очередной);
        ПроверитьСвойАктивныйReader85(Reader, Поток);
        Описание = ПланыОбмена.ПрочитатьОписаниеИзменений(Reader);
        ПроверитьСвоёОписаниеБезConfigChanges85(Описание);
        Reader.ПрерватьЧтение();
    Исключение
        ПрерватьИЗакрытьСвойReaderПослеОшибки85(Reader, Поток, ОписаниеОшибки());
    КонецПопытки;
    Поток.Закрыть();
    ПроверитьСвойНеактивныйReader85(Reader);
    Если МойДругойУзел85.ПолучитьОбъект().НомерПринятого <> Было Тогда
        ВызватьИсключение "Предварительный reader не должен подтвердить приём";
    КонецЕсли;
КонецПроцедуры

Процедура ПринятьСвойПакет85(Кнопка)
    ПроверитьСвойПустойСтенд();
    ОпределитьСвоиУзлы85();
    ПроверитьОписаниеСвоегоПакета85(Входящее85);
    Поток = Новый ЧтениеXML;
    Поток.УстановитьСтроку(Входящее85);
    Reader = ПланыОбмена.СоздатьЧтениеСообщения();
    Попытка
        Если ТипЗнч(Reader) <> Тип("ЧтениеСообщенияОбмена") Тогда
            ВызватьИсключение "Native factory должен вернуть actual reader";
        КонецЕсли;
        Reader.НачатьЧтение(Поток, ДопустимыйНомерСообщения.Очередной);
        ПроверитьСвойАктивныйReader85(Reader, Поток);
        Отправитель = Reader.Отправитель;
        Номер = Reader.НомерСообщения;
        Ack = Reader.НомерПринятого;
        Описание = ПланыОбмена.ПрочитатьИзмененияКонфигурацииИРасширенийКонфигурации(Reader);
        ПроверитьСвоёОписаниеБезConfigChanges85(Описание);
        ПланыОбмена.ПрочитатьИзменения(Reader, 0);
        СсылкаПослеЧтения = ПрочитатьСвоюСтроку85();
        Если МояСторона85 = "A" И СсылкаПослеЧтения <> МояСсылка85 Тогда
            ВызватьИсключение "Ответ B не должен подменить own ссылку A";
        КонецЕсли;
        Reader.ЗакончитьЧтение();
    Исключение
        ПрерватьИЗакрытьСвойReaderПослеОшибки85(Reader, Поток, ОписаниеОшибки());
    КонецПопытки;
    Поток.Закрыть();
    ПроверитьСвойНеактивныйReader85(Reader);
    Если Отправитель <> МойДругойУзел85 Или МойДругойУзел85.ПолучитьОбъект().НомерПринятого <> Номер Тогда
        ВызватьИсключение "Actual FinishedRead не подтверждён своим node received counter";
    КонецЕсли;
    Если МояСторона85 = "A" Тогда
        Если МойНомерОтправки85 = 0 Или Ack < МойНомерОтправки85 Или Ack > МойДругойУзел85.ПолучитьОбъект().НомерОтправленного Тогда
            ВызватьИсключение "Реально принят own ответ, но он не подтверждает сохранённую отправку A; очистка запрещена";
        КонецЕсли;
        МойПодтвержденныйНомер85 = Ack;
    КонецЕсли;
    Состояние85 = "Native FinishedRead " + МояСторона85 + ": received=" + Строка(Номер) + ", actual peer ack=" + Строка(Ack) + ". Own строка=7. Abort после уже импортированных данных не обещает rollback.";
КонецПроцедуры

Процедура ОтветитьТолькоИзСвоегоB85(Кнопка)
    ПроверитьСвоюСторону85("B");
    ПрочитатьСвоюСтроку85();
    Если МойДругойУзел85.ПолучитьОбъект().НомерПринятого <= 0 Тогда
        ВызватьИсключение "B сначала должен закончить actual чтение пакета A; до этого подтверждения нет";
    КонецЕсли;
    Результат = ЗаписатьСвойПакет85(Ложь);
    Если Результат.Ack <> МойДругойУзел85.ПолучитьОбъект().НомерПринятого Тогда
        ВызватьИсключение "Own ответ должен нести настоящий accepted number B из node record";
    КонецЕсли;
    Исходящее85 = Результат.XML;
    Состояние85 = "Собственный ответ B номер " + Строка(Результат.Номер) + ", ack=" + Строка(Результат.Ack) + ". Скопируйте его в остающуюся открытой форму A.Входящее85, затем ПринятьСвойПакет85.";
КонецПроцедуры

Процедура ОчиститьТолькоПодтвержденнуюСвоюСтроку85(Кнопка)
    ПроверитьСвоюСторону85("A");
    Если МояСсылка85 = Неопределено Или МойНомерОтправки85 = 0 Или МойПодтвержденныйНомер85 < МойНомерОтправки85 Тогда
        ВызватьИсключение "Без сохранённой own отправки и actual FinishedRead peer ack очистка запрещена";
    КонецЕсли;
    Если ПрочитатьСвоюСтроку85() <> МояСсылка85 Или МояСсылка85.ПолучитьОбъект().ВерсияДанных <> МояВерсияОтправки85
        Или МойДругойУзел85.ПолучитьОбъект().НомерОтправленного <> МойНомерОтправки85 Тогда
        ВызватьИсключение "После отправки own объект/версия/номер изменились; нельзя удалить новую регистрацию старым ack";
    КонецЕсли;
    ПланыОбмена.УдалитьРегистрациюИзменений(МойДругойУзел85, МояСсылка85);
    Если ПланыОбмена.ИзменениеЗарегистрировано(МойДругойУзел85, МояСсылка85) Тогда
        ВызватьИсключение "Explicit own очистка не подтверждена readback";
    КонецЕсли;
    Если ПрочитатьСвоюСтроку85() <> МояСсылка85 Тогда
        ВызватьИсключение "После очистки регистрации actual собственная запись должна сохраниться";
    КонецЕсли;
    Состояние85 = "Только одна own регистрация удалена после actual ack; сам объект со значением7 сохранён. Если native импорт уже снял её, эта точная cleanup операция идемпотентна. Blanket node/metadata/произвольный номер не использовались.";
КонецПроцедуры

Процедура ПрерватьИЗакрытьСвойReaderПослеОшибки85(Reader, Поток, ИсходнаяОшибка)
    ОшибкиОсвобождения = "";
    Попытка
        Если Reader.ЧтениеXML <> Неопределено Тогда
            Reader.ПрерватьЧтение();
        КонецЕсли;
    Исключение
        ОшибкиОсвобождения = "; прервать reader не удалось: " + ОписаниеОшибки();
    КонецПопытки;
    Попытка
        Поток.Закрыть();
    Исключение
        ОшибкиОсвобождения = ОшибкиОсвобождения + "; закрыть XML reader не удалось: " + ОписаниеОшибки();
    КонецПопытки;
    ВызватьИсключение ИсходнаяОшибка + ОшибкиОсвобождения;
КонецПроцедуры

Процедура ПроверитьСвойАктивныйReader85(Reader, Поток)
    Если Reader.ЧтениеXML <> Поток Или Reader.Отправитель <> МойДругойУзел85 Или Reader.Отправитель = МойЭтотУзел85
        Или Reader.НомерСообщения <= 0 Или Reader.НомерПринятого < 0
        Или Reader.НомерПринятого > МойДругойУзел85.ПолучитьОбъект().НомерОтправленного Тогда
        ВызватьИсключение "Actual reader source/XML/number/own ack не совпали; импорт запрещён";
    КонецЕсли;
КонецПроцедуры

Процедура ПроверитьСвойНеактивныйReader85(Reader)
    Если Reader.ЧтениеXML <> Неопределено Или Reader.Отправитель <> Неопределено
        Или Reader.НомерСообщения <> Неопределено Или Reader.НомерПринятого <> Неопределено Тогда
        ВызватьИсключение "После Finish/Abort native reader должен сбросить свои временные свойства";
    КонецЕсли;
КонецПроцедуры

Процедура ПроверитьСвоёОписаниеБезConfigChanges85(Описание)
    Если ТипЗнч(Описание) <> Тип("ОписаниеИзмененийКонфигурацииВСообщенииОбмена") Тогда
        ВызватьИсключение "Нужен настоящий native description, не искусственные flags";
    КонецЕсли;
    Если Описание.КонфигурацияИзменена Или Описание.РасширенияКонфигурацииИзменены Или Описание.ИзмененныеМетаданныеРасширенийКонфигурацииИзменяютСтруктуруДанных Тогда
        ВызватьИсключение "Свой первоначальный протокол не разрешает config/extensions changes; не продолжайте импорт";
    КонецЕсли;
КонецПроцедуры

Процедура ОтсоединитьИВосстановитьТолькоСвойChildB85(Кнопка)
    ПроверитьСвоюСторону85("B");
    Сохраненный = ПланыОбмена.ГлавныйУзел();
    Если Сохраненный <> МойДругойУзел85 Или Сохраненный.Код <> "A" Или МойЭтотУзел85.Код <> "B" Тогда
        ВызватьИсключение "Изменение главного узла разрешено только actual childB своего A";
    КонецЕсли;
    Попытка
        ПланыОбмена.УстановитьГлавныйУзел(Неопределено);
        Если ПланыОбмена.ГлавныйУзел() <> Неопределено Тогда
            ВызватьИсключение "Own detached промежуточное состояние не подтверждено";
        КонецЕсли;
        ПланыОбмена.УстановитьГлавныйУзел(Сохраненный);
        Если ПланыОбмена.ГлавныйУзел() <> Сохраненный Тогда
            ВызватьИсключение "Own parent не восстановлен";
        КонецЕсли;
    Исключение
        ИсходнаяОшибка = ОписаниеОшибки();
        Текущий = ПланыОбмена.ГлавныйУзел();
        Если Текущий = Неопределено Тогда
            Попытка
                ПланыОбмена.УстановитьГлавныйУзел(Сохраненный);
                Если ПланыОбмена.ГлавныйУзел() <> Сохраненный Тогда
                    ВызватьИсключение "Own восстановление parent не подтверждено";
                КонецЕсли;
            Исключение
                ВызватьИсключение ИсходнаяОшибка + "; own восстановление тоже не удалось: " + ОписаниеОшибки();
            КонецПопытки;
        ИначеЕсли Текущий <> Сохраненный Тогда
            ВызватьИсключение ИсходнаяОшибка + "; обнаружен другой current parent: автоматически не перезаписываем";
        КонецЕсли;
        ВызватьИсключение ИсходнаяОшибка;
    КонецПопытки;
    Состояние85 = "Только own childB временно detached и actual parentA восстановлен. Это не обмен, не ack и не откат данных; исходные ошибки не скрывались.";
КонецПроцедуры

Процедура ПроверитьСвойПустойСтенд()
    Если Метаданные.Справочники.Количество() <> 1 Или Метаданные.Документы.Количество() <> 0 Или Метаданные.Перечисления.Количество() <> 0 Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0 Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0 Или Метаданные.ПланыВидовХарактеристик.Количество() <> 0 Или Метаданные.ПланыВидовРасчета.Количество() <> 0 Или Метаданные.ПланыСчетов.Количество() <> 0 Или Метаданные.ПланыОбмена.Количество() <> 1 Или Метаданные.Последовательности.Количество() <> 0 Или Метаданные.БизнесПроцессы.Количество() <> 0 Или Метаданные.Задачи.Количество() <> 0 Или Метаданные.ОбщиеРеквизиты.Количество() <> 0 Или Метаданные.ПодпискиНаСобытия.Количество() <> 0 Или Метаданные.РегламентныеЗадания.Количество() <> 0 Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 0 Тогда
        ВызватьИсключение "Нужен только свой новый пустой учебный стенд";
    КонецЕсли;
    Если Метаданные.ЕстьИзмененияРасширениямиКонфигурации() Тогда
        ВызватьИсключение "Свой сеанс не должен изменяться расширениями";
    КонецЕсли;
    Источники = Новый Массив;
    Источники.Добавить(ИсточникРасширенийКонфигурации.БазаДанных);
    Источники.Добавить(ИсточникРасширенийКонфигурации.СеансАктивные);
    Источники.Добавить(ИсточникРасширенийКонфигурации.СеансОтключенные);
    Для Каждого Источник Из Источники Цикл
        Расширения = РасширенияКонфигурации.Получить(, Источник);
        Если ТипЗнч(Расширения) <> Тип("Массив") Тогда
            ВызватьИсключение "Native список расширений должен иметь тип Массив";
        КонецЕсли;
        Если Расширения.Количество() <> 0 Тогда
            ВызватьИсключение "В своём новом стенде нет расширений базы или сеанса";
        КонецЕсли;
    КонецЦикла;
    Если ТранзакцияАктивна() Тогда
        ВызватьИсключение "Чужая или вложенная активная транзакция не допускается";
    КонецЕсли;
КонецПроцедуры

#КонецЕсли

Разбор по шагам

  1. ПриОткрытии проверяет exact пустую собственную metadata, единственный RIB состав и запрещённую autoreregistration, два actual узла, native ЭтотУзел и root/child условия. UUID-path guard разбирает собственный File= из текущей строки соединения и разрешает только точное буквальное имя и совпадающее Файл.ПолноеИмя для A/B; серверная или чужая база отклоняется.

  2. A→ПрерватьСвойWriter85 демонстрирует actual Begin/Abort до выбора или записи тела. Сохраняется номер предыдущей отправки; Begin предлагает следующий номер, Abort освобождает writer и не объявляется отправкой. Временные readonly writer свойства после Abort сброшены по native описанию.

  3. A→ЗарегистрироватьТолькоСвоюСтроку85 проверяет всю единственную own запись, сначала отсутствие регистрации, затем вызов ЗарегистрироватьИзменения только для actual nodeB+actual ownref и readback Истина. Это не отправка и не подтверждение доставки.

  4. A→ОтправитьСвоюСтрокуA85: в настоящий writer попадает own nodeB. Пока writer активен, считываются readonly Получатель/ЗаписьXML/НомерСообщения/НомерПринятого. ВыбратьИзменения с фильтром одной actual ссылки читает реальный изменённый объект и проставляет номер в регистрации: эта выборка не read-only. Нативный ЗаписатьИзменения(Writer,0) затем записывает полное RIB тело; вручную дублировать выбранный объект в XML нельзя.

  5. ЗакончитьЗапись делает сообщение считающимся отправленным и обновляет own sent counter; XML строка готова для копирования. Это не доказывает получение B. Номер и собственная версия объекта сохранены до/во время отправки, потому что после Finish временные свойства writer сбрасываются. При ошибке явно Abort только ещё активного writer; регистрационные номера, уже изменённые выборкой, не объявляются откатившимися.

  6. Скопируйте A.Исходящее85 в B.Входящее85. B→ПосмотретьИПрерватьСвойReader85: настоящий reader с допустимым номером Очередной проверяет header/own sender, читает ПрочитатьОписаниеИзменений, требует все три native config/extensions flags Ложь и явно Abort. Номер принятого узла не меняется. После description этот reader НЕЛЬЗЯ использовать для ПрочитатьИзменения.

  7. B→ПринятьСвойПакет85 сначала повторяет отдельный безопасный preview. Для самого импорта создаёт НОВЫЙ reader на том же own XML; active header readonly значения сохраняются заранее. ПрочитатьИзмененияКонфигурацииИРасширенийКонфигурации идёт до ПрочитатьИзменения; если какой-либо config/extension flag не Ложь, дальше не продолжаем. Наши одинаковые A/B не требуют изменения CF или перезапуска.

  8. ПрочитатьИзменения(Reader,0) реально импортирует RIB данные, после чего actual query должен найти одну own запись со всеми точными значениями7/marker. Только затем ЗакончитьЧтение нормально принимает сообщение и обновляет own peer received counter. Отдельный final readback подтверждает этот counter; это не общая транзакция всех reader/DB/config стадий.

  9. B→ОтветитьТолькоИзСвоегоB85 разрешён лишь после actual accepted number>0. Native writer сам помещает этот реальный НомерПринятого в header собственного ответа. Передайте B.Исходящее85 обратно в остающуюся открытой форму A.Входящее85 и выполните ПринятьСвойПакет85. Только после FinishedRead собственного ответа A сохраняет actual ack, подтверждающий её сохранённую отправку и не превышающий реальный sent counter.

  10. A→ОчиститьТолькоПодтвержденнуюСвоюСтроку85 запрещён до actual ack, при новом номере отправки или изменившейся версии own объекта. УдалитьРегистрациюИзменений получает только actual nodeB+одну actual ownref — не весь узел, metadata, Неопределено или пользовательский номер. После этого readback регистрации Ложь, сам объект7 остаётся. Если native импорт уже снял регистрацию, точный cleanup идемпотентен; это прямо отличено от утверждения, будто только данная команда обязательно произвела удаление.

  11. Отдельный опыт только B: ОтсоединитьИВосстановитьТолькоСвойChildB85 сохраняет реальный ГлавныйУзел=A, временно отменяет назначение через Неопределено и восстанавливает тот же actual A. В ошибке автоматическое восстановление разрешено только из собственного промежуточного состояния ГлавныйУзел=Неопределено; если появился другой parent, он не перезаписывается. Исходная ошибка и ошибка восстановления не скрываются. Этот опыт не является доставкой/ack/rollback.

Как проверить результат

  1. В настоящем начальном B native ЭтотУзел=B и ГлавныйУзел=A; обе базы имеют только два own узла. Команда образа второй раз обязана отказать из-за существующей цели. Ничего не удаляется/не перезаписывается.

  2. A registration: Ложь→явная регистрация→Истина. Writer Begin/Abort до тела не меняет sent counter. Нормальная отправка A увеличивает counter на1 и оставляет регистрационное состояние неравнозначным ack.

  3. B preview/Abort не увеличивает accepted counter и не создаёт own строку. Потом новая actual full read/Finish должна создать только запись Э85/Мой обмен85/7/собственный marker и увеличить own accepted counter до номера входящего сообщения.

  4. B reply header carries actual accepted A message; до чтения ответа A очистка запрещена. A после actual FinishedRead ответа может очистить только свою неизменённую отправленную запись; readback регистрации Ложь, число7 и ownref не удалены.

  5. Для безопасного собственного отрицательного опыта: очистка до ack, повторное создание B, запустить A-команду в B, повторно принять тот же пакет с Очередной, поставить неправильный own marker в отдельном disposable стенде. Все нарушения должны прерывать дальнейшие стадии. Не используйте чужой XML/реальный узел для негативного опыта.

  6. Если произошла ошибка после ПрочитатьИзменения, проверьте реальные данные и node counters отдельно: Abort не обещает отменить уже импортированные записи. Не повторяйте сообщение с Любой и не вручную обнуляйте counters для обхода ошибки. Собственные A/B должны быть приведены пользователем к понятному проверенному состоянию.

  7. B detach/restore завершается с тем же actual parentA. При исключении убедитесь, какой parent реально остался; сообщение не должно скрыть частичное изменение или утверждать автоматический rollback. Никакого рабочего parent код не выбирает и не перезаписывает.

Ожидаемый результат

Полный собственный RIB lifecycle с actual A/B образа, реальной регистрацией/выборкой, native XML writer/reader и только подтверждённым завершённым ответом, дающим право на точную очистку собственной неизменённой записи.

На что обратить внимание

  1. СоздатьНачальныйОбраз действительно создаёт базу и удаляет регистрацию изменений для данного узла. Это high-impact native операция, разрешённая здесь только для новой собственной CF и несуществующего точного UUID B. Агент ничего не создавал/копировал/удалял.

  2. ВыбратьИзменения проставляет номер в регистрациях при обходе; это не немутирующая диагностическая выборка. Abort writer до тела и Abort после уже выполненной выборки — разные состояния; обратный rollback регистрационных номеров не обещается.

  3. ПрочитатьОписаниеИзменений делает данный reader непригодным для ПрочитатьИзменения: всегда новый reader для actual import. ПрочитатьИзмененияКонфигурацииИРасширенийКонфигурации допускает последующий import только если не было изменений/исключений; наш protocol требует три flags Ложь.

  4. Параметр ЭлементовВТранзакции=0 относится к данным внутри отдельного native WriteChanges/ReadChanges. Это НЕ доказательство одной транзакции header, configuration updates, импортированных данных, FinishRead, регистрации и последующей cleanup.

  5. ПрерватьЧтение снимает блокировку и не меняет запись узла. Не расширяйте это на rollback всех уже импортированных данных или изменений конфигурации. FinishRead отдельно подтверждает нормальный конец XML тела; отсутствие полного конца не даёт подтверждённого приема.

  6. Временные свойства writer/reader действуют только между Begin и Finish/Abort. Writer НомерПринятого после завершения=0; прочие временные поля сбрасываются в Неопределено. Сохраняйте actual значения заранее, не используйте сброшенные поля для ack.

  7. Число во втором параметре УдалитьРегистрациюИзменений удаляет регистрации этого узла с номером <= указанного, а Неопределено может удалить все. Здесь оба blanket варианта намеренно не используются: только точная собственная ссылка и реальный ack+version gate.

  8. Обмен читает только XML из собственной второй формы; этот пример не является безопасным приемником произвольных недоверенных payload из сети. Общее криптографическое подтверждение доставки, подписи, повторы и конфликтные версии здесь не реализованы и не обещаются.

  9. Source/config/production только read-only. Изготовлены лишь lesson/test источники и статические проверки; реальные 1С, DB, UI, files, сеть и native начальный образ агентом не запускались. Новых backups/archives не создавалось.

Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.

Не хотите разбираться сами?

Адаптируем это решение под вашу базу. Оценка бесплатно.

Оставить задачу

1С, 1С:Предприятие — товарные знаки ООО «1С». Сайт не является официальным представительством фирмы «1С» и не аффилирован с ней.