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