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