При разработке внешних обработок заполнения или загрузки данных в управляемом приложении 1С 8.3 программисты часто сталкиваются с классической задачей: необходимо программно записать или изменить документ на сервере, а затем открыть его форму на клиенте. Ситуация усложняется, если форма этого документа уже открыта у пользователя — в таком случае простое повторное открытие может не обновить данные на экране, а неаккуратный код вернет ошибку выполнения.
В этой статье мы подробно проанализируем типичные ошибки, возникающие при работе с формами документов из внешних обработок, разберем причины их появления и рассмотрим наиболее эффективные, соответствующие стандартам разработки фирмы «1С» способы решения этой задачи.
Прежде чем перейти к правильным методикам, давайте проанализируем ошибки из практики разработчиков, чтобы понять природу их возникновения.
1. Ошибка обращения к свойству «ЭтотОбъект» на клиенте
В коде обычного приложения (толстый клиент) конструкция вида ФормаДок.ЭтотОбъект.Прочитать() была стандартной практикой. Однако в управляемом приложении на тонком клиенте объект формы имеет тип УправляемаяФорма, у которого нет свойства ЭтотОбъект. Попытка обратиться к нему на клиенте гарантированно вызовет исключение:
Значение не является значением объектного типа (ЭтотОбъект)
Для перечитывания данных из базы напрямую в управляемой форме используется метод самой формы: ФормаДок.Прочитать(), без каких-либо промежуточных обращений к объекту.
2. Использование устаревшего метода «ПолучитьФорму»
Часто в коде можно встретить следующую конструкцию:
ФормаДок = ПолучитьФорму("Документ.Поступление.ФормаОбъекта", Новый Структура("Ключ", СсылкаНаДокумент));
ФормаДок.Открыть();
Согласно стандартам разработки «1С:Предприятие 8», использование метода ПолучитьФорму крайне не рекомендуется. Этот метод работает медленнее, так как принудительно создает контекст формы на клиенте еще до момента ее отображения. Кроме того, это приводит к избыточным серверным вызовам.
3. Проблема обновления табличной части при использовании «ОткрытьЗначение»
Метод ОткрытьЗначение(СсылкаНаДокумент) является быстрым и простым способом показать объект пользователю. Однако, если форма этого документа уже открыта на экране и в ней содержатся измененные пользователем (но еще не записанные) данные, этот метод просто активирует существующее окно, не обновляя табличные части из базы данных. Вызов метода ОповеститьОбИзменении() также не гарантирует мгновенного принудительного перечитывания данных, если форма находится в режиме редактирования.
Разберем по шагам правильные и надежные сценарии решения этой задачи.
Для открытия формы существующего документа или активации уже открытого окна наиболее правильным решением является использование стандартного метода ОткрытьФорму() с передачей уникального ключа объекта. Платформа самостоятельно анализирует запущенные окна, находит нужную форму по ключу и активирует ее без повторного создания контекста.
Рассмотрим пример кода на клиенте во внешней обработке:
&НаКлиенте
Процедура ВыполнитьЗагрузку(Команда)
// Выполняем программное создание или изменение документа на сервере
СсылкаНаДокумент = ЗаписатьДокументНаСервере();
Если ЗначениеЗаполнено(СсылкаНаДокумент) Тогда
// Формируем параметры для открытия формы. Ключ - это ссылка на наш документ
ПараметрыФормы = Новый Структура("Ключ", СсылкаНаДокумент);
// Платформа сама найдет открытую форму или создаст новую
ОткрытьФорму("Документ.ПоступлениеТоваровУслуг.ФормаОбъекта", ПараметрыФормы);
КонецЕсли;
КонецПроцедуры
Этот способ является максимально производительным и безопасным. Но что делать, если документ уже был открыт на экране, мы изменили его данные на сервере (например, загрузили новую табличную часть из файла), и нам нужно гарантированно обновить отображение этих данных у пользователя? Рассмотрим следующий метод.
Если нам необходимо принудительно обновить данные в уже открытой форме документа из нашей внешней обработки, мы можем получить список открытых окон приложения, найти среди них форму нашего документа и вызвать метод Прочитать(). Это заставит форму перечитать актуальное состояние объекта из базы данных.
Проанализируем пример реализации такого алгоритма:
&НаКлиенте
Процедура ОбновитьОткрытуюФормуДокумента(СсылкаНаДокумент)
// Получаем все открытые окна клиента
ОкнаПриложения = ПолучитьОкна();
Для Каждого ТекущееОкно Из ОкнаПриложения Цикл
// Проверяем, что это окно содержит форму
СодержимоеОкна = ТекущееОкно.ПолучитьСодержимое();
Если СодержимоеОкна <> Неопределено
И ТипЗнч(СодержимоеОкна) = Тип("УправляемаяФорма") Тогда
// Проверяем, относится ли форма к нужному типу документа
Если Найти(СодержимоеОкна.ИмяФормы, "Документ.ПоступлениеТоваровУслуг.ФормаОбъекта") > 0 Тогда
// Проверяем, что в форме открыт именно наш документ
Если СодержимоеОкна.Объект.Ссылка = СсылкаНаДокумент Тогда
// Вызываем метод перечитывания данных из БД
СодержимоеОкна.Прочитать();
Возврат; // Форма найдена и обновлена, выходим из цикла
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
// Если форма не была открыта ранее, просто открываем ее стандартным путем
ПараметрыФормы = Новый Структура("Ключ", СсылкаНаДокумент);
ОткрытьФорму("Документ.ПоступлениеТоваровУслуг.ФормаОбъекта", ПараметрыФормы);
КонецПроцедуры
Важный нюанс: метод Прочитать() сработает успешно только в том случае, если пользователь не успел внести в форму свои не записанные изменения. Если форма модифицирована, платформа выдаст предупреждение о необходимости сохранения данных.
В типовых конфигурациях 1С (особенно построенных на базе Библиотеки стандартных подсистем — БСП) для обновления открытых форм принято использовать механизм глобальных оповещений. Этот способ считается наиболее изящным и архитектурно верным.
Суть метода заключается в том, что внешняя обработка посылает клиенту сигнал о том, что документ был изменен, а открытая форма документа ловит этот сигнал и сама решает, как ей обновиться. Разберем этот процесс по шагам.
Шаг 1. Отправка оповещения из внешней обработки
После успешной записи документа на сервере в модуле управляемой формы вашей обработки на клиенте вызывается метод Оповестить:
&НаКлиенте
Процедура ПослеЗаписиДокумента(СсылкаНаДокумент)
// Отправляем глобальное оповещение по всей системе
Оповестить("ЗаписанДокументПоступления", СсылкаНаДокумент, ЭтаФорма);
КонецПроцедуры
Шаг 2. Обработка события в форме самого документа
В модуле формы документа (в данном случае «Поступление товаров и услуг») должен быть определен предопределенный обработчик события ОбработкаОповещения:
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
// Проверяем, наше ли это событие и относится ли оно к текущему документу
Если ИмяСобытия = "ЗаписанДокументПоступления" И Параметр = Объект.Ссылка Тогда
// Если форма не изменена пользователем вручную, перечитываем данные
Если Не Модифицированность Тогда
ЭтаФорма.Прочитать();
Иначе
ПоказатьПредупреждение(, "Данные документа были изменены внешней обработкой. Пожалуйста, перечитайте документ.");
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Если вы разрабатываете собственную конфигурацию или дорабатываете существующую (например, через расширение), обязательно внедряйте этот паттерн. Он исключает жесткую связь между кодом внешней обработки и внутренним устройством формы документа.
Иногда разработчики идут по пути: «создать пустой документ на сервере — записать его — открыть форму для заполнения». Это приводит к засорению информационной базы пустыми или ошибочно созданными документами, если пользователь в итоге решит закрыть форму без сохранения.
Гораздо более дружелюбным подходом к интерфейсу будет открытие формы нового (еще незаписанного) документа, параметры которого уже предзаполнены данными из нашей обработки. Для этого используется стандартный параметр формы ЗначенияЗаполнения.
Рассмотрим, как это реализовать:
&НаКлиенте
Процедура ОткрытьНовыйДокументСЗаполнением()
// Формируем структуру параметров заполнения
ДанныеЗаполнения = Новый Структура;
ДанныеЗаполнения.Вставить("Организация", Объект.Организация);
ДанныеЗаполнения.Вставить("Склад", Объект.Склад);
ДанныеЗаполнения.Вставить("Комментарий", "Загружено из внешнего файла");
// Помещаем структуру параметров заполнения в параметры открытия формы
ПараметрыОткрытия = Новый Структура;
ПараметрыОткрытия.Вставить("ЗначенияЗаполнения", ДанныеЗаполнения);
// Открываем форму нового документа
ОткрытьФорму("Документ.ПоступлениеТоваровУслуг.ФормаОбъекта", ПараметрыОткрытия);
КонецПроцедуры
При таком подходе платформа 1С автоматически инициализирует новый объект на сервере, вызовет стандартный обработчик события ОбработкаЗаполнения в модуле документа и откроет пользователю заполненную форму. Если пользователь нажмет кнопку «Закрыть» и откажется от сохранения, в базе данных не останется лишнего «мусора».
Для качественной работы внешних обработок в управляемом приложении 1С 8.3 придерживайтесь следующих правил:
ЭтотОбъект на клиенте в управляемых формах.ПолучитьФорму в пользу современного ОткрытьФорму() с передачей ключа структуры.Прочитать() непосредственно у объекта формы или реализуйте механизм оповещений через процедуру Оповестить().ЗначенияЗаполнения вместо предварительной записи пустого объекта на сервере.