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