При разработке и использовании внешних обработок в 1С:Предприятии 8.3 на управляемых формах часто возникает ситуация, когда внешняя обработка изменяет данные документа в базе, но открытая форма этого документа не отображает внесенные изменения. Пользователю приходится закрывать и снова открывать документ, чтобы увидеть актуальные данные. Рассмотрим, почему это происходит и как правильно организовать мгновенное обновление формы.
Когда вы открываете форму документа в 1С, ее данные загружаются из базы данных и кэшируются. Все элементы управления на форме (поля, таблицы, списки) отображают эти кэшированные данные. Если внешняя обработка изменяет тот же документ напрямую в базе данных, открытая форма не "знает" об этих изменениях автоматически. Она продолжает отображать старые, кэшированные данные.
Важно понимать различие между следующими методами:
ОбновитьОтображениеДанных(): Этот метод обновляет только отображение элементов управления на форме. Он не перечитывает данные самого объекта из базы данных. Если данные объекта в базе изменились, а в форме нет, этот метод не поможет.ОповеститьОбИзменении(Ссылка): Этот механизм предназначен для оповещения системы о том, что объект по данной ссылке изменился. Он может быть использован для обновления динамических списков или запуска обработчиков в других формах, но сам по себе он не гарантирует полное перечитывание данных всей формы объекта.Основная причина, по которой ваши попытки обновить форму (как в сообщении 8) могли не сработать, заключается либо в том, что сама модификация данных в базе не произошла корректно, либо в том, что был вызван неправильный метод для перезагрузки данных формы.
Прежде чем пытаться обновить форму, мы должны быть абсолютно уверены, что внешняя обработка действительно изменила и записала документ в базе данных. В сообщении 15 была выявлена очень распространенная ошибка: попытка вызвать метод ПолучитьОбъект() у переменной, которая на самом деле является строкой, а не ссылкой 1С.
В 1С существует принципиальная разница между ДокументСсылка.ИмяДокумента (ссылка на данные) и ДокументОбъект.ИмяДокумента (объект, который можно изменять и записывать).
ПолучитьОбъект().Если в вашей обработке вы передаете ссылку на документ как строку (например, из-за особенностей тестирования или передачи параметров), вы не сможете вызвать ПолучитьОбъект() напрямую. Сначала нужно преобразовать строку в полноценную ссылку 1С:
// Пример из сообщения 15, где 'Объект.СсылкаНаОбъект' был строкой
// НЕПРАВИЛЬНО:
// ДокОбъект = Объект.СсылкаНаОбъект.ПолучитьОбъект();
// ПРАВИЛЬНО: Сначала преобразуем строку в ссылку 1С, затем получаем объект.
// Предположим, строка содержит UUID ссылки или другое строковое представление,
// которое можно преобразовать в Ссылку 1С.
// В данном случае, если это просто строка ссылки (например, "ДокументСсылка.РеализацияТоваровУслуг ГУ00000001"),
// то можно использовать функцию ПолучитьСсылкуИзСтроки (ее может не быть, это пример).
// Более универсальный подход – если у вас есть только строковое представление UUID:
// Пример 1: Если у вас есть строковое представление UUID
// Допустим, Объект.СсылкаНаОбъект = "e87d3a01-b2c3-4d4e-a1b2-c3d4e5f6a7b8"
// ИЛИ
// Допустим, Объект.СсылкаНаОбъект = "ДокументСсылка.ИмяДокумента,e87d3a01-b2c3-4d4e-a1b2-c3d4e5f6a7b8"
// Вам нужно знать тип ссылки для ее восстановления.
// Вариант, если строка содержит полный строковый идентификатор ссылки
// Например, "{"#",6b7c8d9e-0123-4567-89ab-cdef01234567,1,Документ.РеализацияТоваровУслуг}"
// В реальной жизни лучше передавать ссылку, а не её строковое представление для работы
// Но если уж так получилось, что пришла строка, можно использовать ЗначениеИзСтрокиВнутр()
// или, чаще всего, просто передавать саму ссылку в параметрах.
// Если вы открываете форму обработки из формы документа и передаете ссылку:
// Например, в форме документа:
// ПараметрыФормы = Новый Структура("СсылкаНаДокумент", Объект.Ссылка);
// ОткрытьФорму("ВнешняяОбработка.ИмяОбработки.Форма", ПараметрыФормы, ЭтаФорма);
// Тогда в обработке вы получите ссылку:
// СсылкаНаДокумент = Параметры.СсылкаНаДокумент;
// ДокОбъект = СсылкаНаДокумент.ПолучитьОбъект(); // Вот это будет работать!
// ... выполняем изменения ...
// ДокОбъект.Записать(); // ЗАПИСЫВАЕМ изменения в базу!
// Если же у вас всё-таки есть строковое представление ссылки, которое нужно преобразовать,
// и вы знаете тип документа:
ТипДокумента = Метаданные.Документы.ИмяВашегоДокумента.СсылкаНаОбъект; // Получаем тип ссылки
// Пример, если строка содержит UUID:
// СсылкаСтрокой = Объект.СсылкаНаОбъект; // Пусть это будет строка UUID
// Если это строковое представление ссылки, которое пригодно для получения значения:
// Допустим, вы передали Ссылка.УникальныйИдентификатор в виде строки
// ДокСсылка = Новый УникальныйИдентификатор(СсылкаСтрокой);
// ДокОбъект = ДокСсылка.ПолучитьОбъект(); // Это не будет работать, нужен тип ссылки.
// Корректный способ получить объект, если вы передали СсылкуНаОбъект как строковый идентификатор:
// Обычно для этого в обработку передают не просто строку, а объект-ссылку.
// Если же вы всё равно получили строку, то наиболее надежный способ (если строка - это UUID)
// и вы знаете тип объекта:
// СсылкаНаОбъектСтрока = Объект.СсылкаНаОбъект; // Предположим, это GUID в виде строки
// ТипОбъектаМетаданных = Метаданные.Документы.ИмяВашегоДокумента;
// Если ТипОбъектаМетаданных <> Неопределено Тогда
// ДокСсылка = Документы.ИмяВашегоДокумента.ПолучитьСсылкуПоУникальномуИдентификатору(Новый УникальныйИдентификатор(СсылкаНаОбъектСтрока));
// Если ДокСсылка.Пустая() Тогда
// // Ссылка не найдена или неверный GUID
// Иначе
// ДокОбъект = ДокСсылка.ПолучитьОбъект();
// // ... работа с ДокОбъект ...
// ДокОбъект.Записать();
// КонецЕсли;
// КонецЕсли;
// В контексте сообщения 15, если `Объект.СсылкаНаОбъект` - это реквизит формы обработки типа Строка,
// и в него записывается строковое представление ссылки документа, то:
// Предположим, вы передали "ДокументСсылка.ИмяДокумента|ГУИД" или "ГУИД"
// Правильнее всего в обработку передавать непосредственно Ссылку.
// Если же это строка, то ее нужно правильно разобрать:
// Например, если строка содержит полный путь к ссылке, как в Отладчике:
// Пример строки: "ДокументСсылка.РеализацияТоваровУслуг,00000000-0000-0000-0000-000000000000"
// Можно попробовать УниверсальныеМеханизмы.ПолучитьОбъектПоСтроковомуПредставлению(СтрокаСсылки)
// (если такая функция есть в вашей конфигурации).
// ИЛИ, как было сказано, передать саму ссылку в параметрах при открытии обработки:
// В форме документа:
// ФормаОбработки = ПолучитьФорму("ВнешняяОбработка.ИмяОбработки.Форма", , ЭтаФорма);
// ФормаОбработки.СсылкаНаОбрабатываемыйОбъект = Объект.Ссылка; // Предполагается реквизит у обработки
// ФормаОбработки.Открыть();
// В форме обработки:
// Процедура ПослеОткрытия()
// Если СсылкаНаОбрабатываемыйОбъект <> Неопределено Тогда
// ДокОбъект = СсылкаНаОбрабатываемыйОбъект.ПолучитьОбъект();
// // ...
// КонецЕсли;
// КонецПроцедуры
//
Суть: Убедитесь, что вы работаете с переменной типа "Ссылка", чтобы получить "Объект" с помощью ПолучитьОбъект(). Только после успешного получения объекта и вызова Записать() изменения будут сохранены в базе данных.
После того, как внешняя обработка успешно изменила и записала документ в базе данных, мы можем приступить к обновлению открытой формы документа. Существует несколько методов, которые можно использовать в зависимости от контекста вызова обработки и структуры форм.
Прочитать() для формы-владельцаЭтот способ наиболее прост и эффективен, если внешняя обработка открывается из формы документа, и форма документа является ее владельцем. В этом случае мы можем напрямую обратиться к форме-владельцу и вызвать ее метод Прочитать(), который перечитает данные объекта, лежащего в основе формы, из базы данных и обновит все связанные с ним элементы формы.
Порядок действий:
// В модуле формы документа, откуда открывается обработка
&НаКлиенте
Процедура ОткрытьВнешнююОбработку(Команда)
ФормаОбработки = ПолучитьФорму("ВнешняяОбработка.ИмяВашейОбработки.Форма", Новый Структура(), ЭтаФорма);
ФормаОбработки.Открыть();
КонецПроцедуры
Прочитать():
// В модуле внешней обработки
&НаКлиенте
Процедура ЗавершитьРаботу(Команда)
// ... здесь происходит изменение ДокОбъект и ДокОбъект.Записать() ...
// Проверяем, есть ли владелец формы и вызываем его метод Прочитать()
Если ЭтаФорма.ВладелецФормы <> Неопределено Тогда
ЭтаФорма.ВладелецФормы.Прочитать();
КонецЕсли;
// Если нужно, закрываем форму обработки
Закрыть();
КонецПроцедуры
Метод Прочитать() является мощным инструментом, так как он фактически пересоздает объект данных формы на основе текущих данных из базы, гарантируя полное обновление всех привязанных к данным элементов управления.
ОповеститьОбИзменении() и ОбработкаОповещения()Этот метод более универсален и подходит для случаев, когда внешняя обработка не является непосредственным "потомком" формы документа, или когда нужно обновить несколько форм. Он работает по принципу "извещения" системы о том, что некий объект изменился, и формы, которые "слушают" такие извещения, могут на это отреагировать.
Порядок действий:
ОповеститьОбИзменении(), передавая ссылку на измененный объект:
// В модуле внешней обработки
&НаКлиенте
Процедура ЗавершитьРаботу(Команда)
// ... здесь происходит изменение ДокОбъект и ДокОбъект.Записать() ...
// Оповещаем систему об изменении документа
ОповеститьОбИзменении(ДокОбъект.Ссылка);
// Можно также использовать более специфичное оповещение:
// Оповестить("ОбновлениеДанныхДокумента", ДокОбъект.Ссылка);
// Если нужно, закрываем форму обработки
Закрыть();
КонецПроцедуры
ОбработкаОповещения. Этот обработчик будет "ловить" оповещения и выполнять необходимые действия для обновления формы.
// В модуле формы документа
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
// Если было вызвано ОповеститьОбИзменении(Ссылка) без ИмениСобытия:
Если ТипЗНачения(Параметр) = Тип("ДокументСсылка.ИмяВашегоДокумента") Тогда
// Проверяем, что измененный объект - это текущий документ
Если Параметр = Объект.Ссылка Тогда
ЭтаФорма.Прочитать(); // Перечитываем данные формы из базы
// Также можно обновить отображение данных после прочтения, если нужно
ЭтаФорма.ОбновитьОтображениеДанных();
КонецЕсли;
КонецЕсли;
// Если было вызвано Оповестить("ОбновлениеДанныхДокумента", Ссылка):
Если ИмяСобытия = "ОбновлениеДанныхДокумента" Тогда
Если ТипЗНачения(Параметр) = Тип("ДокументСсылка.ИмяВашегоДокумента") Тогда
Если Параметр = Объект.Ссылка Тогда
ЭтаФорма.Прочитать(); // Перечитываем данные формы из базы
ЭтаФорма.ОбновитьОтображениеДанных();
КонецЕсли;
КонецЕсли;
КонецЕсли;
// Если нужно обновить динамический список на форме
// Например, если внешняя обработка меняет связанные объекты,
// которые отображаются в списке:
// Если ИмяСобытия = "ОбновлениеСвязанныхДанных" Тогда
// Элементы.ВашДинамическийСписок.Обновить();
// КонецЕсли;
КонецПроцедуры
Использование именованных оповещений (например, Оповестить("ОбновлениеДанныхДокумента", ...)) позволяет более гибко реагировать на различные события и избегать случайных срабатываний.
Если внешняя обработка изменяет не сам документ, а только данные, которые отображаются в динамическом списке на форме документа (например, связанные табличные части других документов или регистры), то может быть достаточно обновить только этот динамический список. Это особенно актуально, когда сам документ не меняется, но меняется состав связанных с ним элементов.
В этом случае, в обработчике ОбработкаОповещения формы документа, вместо ЭтаФорма.Прочитать(), вы вызываете метод Обновить() для конкретного элемента динамического списка:
// В модуле формы документа
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
// Например, если внешняя обработка изменила записи регистра,
// которые отображаются в динамическом списке "СписокСвязанныхДокументов"
Если ИмяСобытия = "ИзменилисьСвязанныеДанные" Тогда
Элементы.СписокСвязанныхДокументов.Обновить();
КонецЕсли;
КонецПроцедуры
Этот подход более "легкий" по сравнению с полным перечитыванием формы, так как он обновляет только часть данных, не перезагружая всю форму документа.
Для корректной работы внешней обработки и возможности обновления формы документа, очень важно правильно передавать ссылку на обрабатываемый документ в обработку. Наиболее надежные способы:
// В модуле формы документа
&НаКлиенте
Процедура ОткрытьОбработкуПоДокументу(Команда)
ПараметрыФормы = Новый Структура();
ПараметрыФормы.Вставить("ДокументСсылка", Объект.Ссылка);
ФормаОбработки = ПолучитьФорму("ВнешняяОбработка.ИмяВашейОбработки.Форма", ПараметрыФормы, ЭтаФорма);
ФормаОбработки.Открыть();
КонецПроцедуры
// В модуле формы внешней обработки (в событии ПриСозданииНаСервере или ПриОткрытии)
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если Параметры.Свойство("ДокументСсылка") Тогда
ЭтаФорма.ДокументСсылка = Параметры.ДокументСсылка; // Сохраняем ссылку в реквизит формы обработки
КонецЕсли;
КонецПроцедуры
// Затем в обработке используем ЭтаФорма.ДокументСсылка для получения объекта:
// ДокОбъект = ЭтаФорма.ДокументСсылка.ПолучитьОбъект();
ЭтаФорма.ВладелецФормы из обработки.Вернемся к вашему коду из сообщения 8, чтобы понять, почему он не сработал:
&НаКлиенте
Процедура Заполнить(Команда)
Вывести; // Предполагается, что здесь происходит изменение и запись в БД
ОповеститьОбИзменении(Объект.СсылкаНаОбъект); // Попытка оповестить
ОтобразитьИзменениеДанных(Объект.СсылкаНаОбъект,ВидИзмененияДанных.Изменение) ; // Оповещение об изменении данных
ОбновитьОтображениеДанных; // Обновление текущей формы обработки
ЭтаФорма.ОбновитьОтображениеДанных; // То же самое
Закрыть;
КонецПроцедуры
Проанализируем ситуацию:
Вывести;: Если в этой процедуре вы пытались получить объект из Объект.СсылкаНаОбъект (который оказался строкой, а не ссылкой) и вызвать ПолучитьОбъект(), то эта операция завершалась ошибкой или не выполнялась вовсе. Следовательно, изменения не записывались в базу данных. Это была критическая проблема, на которую указали в сообщении 15. Если документ не был фактически изменен в базе, то никакие оповещения и обновления формы не помогут.ОповеститьОбИзменении(Объект.СсылкаНаОбъект);: Если Объект.СсылкаНаОбъект являлся строкой, то этот вызов либо ничего не делал, либо приводил к ошибке типа данных, и оповещение не происходило. Даже если бы оповещение прошло, то без соответствующего обработчика ОбработкаОповещения в форме документа, она бы на него не отреагировала.ОтобразитьИзменениеДанных(...): Этот метод в основном используется для управления кэшем представления ссылок и обновлением динамических списков, связанных с данным типом данных, но он не приводит к полному перечитыванию формы документа.ОбновитьОтображениеДанных; и ЭтаФорма.ОбновитьОтображениеДанных;: Эти вызовы обновляют только элементы управления текущей формы (в данном случае - формы обработки), но не перезагружают данные из базы. Даже если бы эти методы были вызваны в форме документа, они бы обновили только визуальное представление, не изменив внутренние данные формы.Таким образом, ваш код не работал по нескольким причинам: основной проблемой было некорректное получение объекта для модификации, что приводило к отсутствию изменений в базе. Даже если бы изменения были, механизмы оповещения были либо неверно использованы, либо не имели соответствующего обработчика, а методы обновления отображения не предназначены для перезагрузки данных формы из базы.
Для успешного обновления данных формы документа 1С после применения внешней обработки необходимо выполнить следующие ключевые шаги:
Записать() для сохранения изменений в базе данных.ЭтаФорма.ВладелецФормы.Прочитать().ОповеститьОбИзменении(СсылкаНаДокумент) во внешней обработке и реализуйте обработчик ОбработкаОповещения в модуле формы документа, который вызовет ЭтаФорма.Прочитать() или Элементы.ВашДинамическийСписок.Обновить() для конкретных элементов.Следуя этим рекомендациям, вы сможете добиться мгновенного обновления данных формы документа после работы внешней обработки, значительно улучшив пользовательский опыт.