При разработке складских или торговых рабочих мест на платформе 1С программисты часто сталкиваются с задачей визуализации связанных бизнес-процессов. Классический пример: вывод основного документа (например, ЗаказПокупателя) в строках журнала, а в динамически созданных колонках — связанных с ним документов выполнения задач (например, СборкаПоСекции). При этом менеджеры или кладовщики хотят видеть интерактивную карту сборки: если по ячейке сборки кликнуть дважды, должен открыться именно этот подчиненный документ, а не основной заказ.
В этой статье мы подробно разберем, почему возникает путаница с получением ссылки на документ при двойном клике в обычных формах, и рассмотрим несколько надежных архитектурных решений этой проблемы как для обычных, так и для современных управляемых форм.
В обычных формах элемент управления ТабличноеПоле, отображающий журнал документов или динамический список, жестко связан со своей основной таблицей БД. Когда мы программно добавляем колонки для отображения статусов сборки по секциям и наполняем их через обработчик ПриВыводеСтроки, эти данные существуют исключительно на слое визуального отображения (презентации). Сама платформа 1С в оперативной памяти списка эти данные не хранит.
Именно поэтому, когда пользователь дважды кликает по динамической ячейке, стандартное событие Выбор ведет себя следующим образом:
ВыбраннаяСтрока содержит ссылку только на строку основного источника (то есть на ЗаказПокупателя).Элемент.Значение для динамической колонки возвращает пустую ссылку или тип колонки (например, описание типа ЖурналДокументовСсылка), так как физической связи с реквизитами БД в этой ячейке нет.Ниже мы детально проанализируем три практических способа решения этой задачи.
---
Этот метод является наиболее надежным и производительным. Чтобы не перегружать базу данных постоянными запросами при интерактивных действиях пользователя, мы сохраним связь между заказами, секциями и документами сборки в специальной переменной-кэше в памяти формы.
Для оптимизации работы интерфейса вместо обработчика ПриВыводеСтроки мы задействуем обработчик ПриПолученииДанных. Он отрабатывает один раз для всей видимой на экране пачки строк, что позволяет получить все нужные документы сборок одним пакетным запросом.
Разберем по шагам реализацию этого механизма в модуле формы журнала:
Шаг 1. Объявление и инициализация кэша
В самом верху модуля формы объявим переменную для кэша:
Перем КэшДокументовСборки; // Будет хранить Соответствие: Ключ (Строка "Заказ_Секция") -> Значение (Ссылка на Сборку)
Инициализируем её при открытии нашей формы:
Процедура ПриОткрытии()
КэшДокументовСборки = Новый Соответствие;
КонецПроцедуры
Шаг 2. Заполнение кэша и раскраска в событии ПриПолученииДанных
Создадим процедуру-обработчик для табличного поля списка документов. Здесь мы собираем ссылки на все видимые заказы, ищем для них документы сборки и записываем результаты в кэш:
Процедура ДокументыСписокПриПолученииДанных(Элемент, ОформленияСтрок)
// Шаг 2.1. Собираем массив уникальных ссылок на заказы, отображаемые на экране
МассивЗаказов = Новый Массив;
Для Каждого ОформлениеСтроки Из ОформленияСтрок Цикл
Если ОформлениеСтроки.ДанныеСтроки = Неопределено Тогда
Продолжить;
КонецЕсли;
МассивЗаказов.Добавить(ОформлениеСтроки.ДанныеСтроки.Ссылка);
КонецЦикла;
Если МассивЗаказов.Количество() = 0 Тогда
Возврат;
КонецЕсли;
// Шаг 2.2. Запросом выбираем документы сборки по этим заказам
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Сборка.Ссылка КАК Ссылка,
| Сборка.ЗаказПокупателя КАК ЗаказПокупателя,
| Сборка.Секция КАК Секция
|ИЗ
| Документ.СборкаПоСекции КАК Сборка
|ГДЕ
| Сборка.ЗаказПокупателя В (&МассивЗаказов)
| И Сборка.Проведен";
Запрос.УстановитьПараметр("МассивЗаказов", МассивЗаказов);
Выборка = Запрос.Выполнить().Выбрать();
// Заполняем кэш уникальными ключами
Пока Выборка.Следующий() Цикл
// Формируем уникальный строковый ключ для кэша
КлючСвязи = Строка(Выборка.ЗаказПокупателя.УникальныйИдентификатор()) + "_" + СокрЛП(Выборка.Секция);
КэшДокументовСборки.Вставить(КлючСвязи, Выборка.Ссылка);
КонецЦикла;
// Шаг 2.3. Оформляем ячейки на основе данных из кэша
Для Каждого ОформлениеСтроки Из ОформленияСтрок Цикл
Если ОформлениеСтроки.ДанныеСтроки = Неопределено Тогда
Продолжить;
КонецЕсли;
СсылкаНаЗаказ = ОформлениеСтроки.ДанныеСтроки.Ссылка;
// Перебираем колонки табличного поля, отвечающие за секции сборки
Для Каждого Колонка Из Элемент.Колонки Цикл
// Предположим, колонки секций имеют префикс "Секция_" (например, "Секция_А", "Секция_Б")
Если Найти(Колонка.Имя, "Секция_") = 1 Тогда
ИмяСекции = СтрЗаменить(Колонка.Имя, "Секция_", "");
// Получаем ключ для текущей ячейки
КлючСвязи = Строка(СсылкаНаЗаказ.УникальныйИдентификатор()) + "_" + ИмяСекции;
СсылкаНаСборку = КэшДокументовСборки[КлючСвязи];
Если ЗначениеЗаполнено(СсылкаНаСборку) Тогда
ОформлениеСтроки.Ячейки[Колонка.Имя].УстановитьТекст("Собрано");
ОформлениеСтроки.Ячейки[Колонка.Имя].ЦветФона = WebЦвета.СветлоЗеленый;
Иначе
ОформлениеСтроки.Ячейки[Колонка.Имя].УстановитьТекст("Ожидает");
ОформлениеСтроки.Ячейки[Колонка.Имя].ЦветФона = WebЦвета.СветлоРозовый;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЦикла;
КонецПроцедуры
Шаг 3. Перехват события Выбор (Двойной клик)
Теперь напишем обработчик клика по ячейке. Если пользователь кликает по колонке секции, мы отменяем открытие заказа покупателя и открываем документ сборки из нашего кэша:
Процедура ДокументыСписокВыбор(Элемент, Колонка, СтандартнаяОбработка)
// Проверяем, что кликнули по колонке секции
Если Найти(Колонка.Имя, "Секция_") = 1 Тогда
// Отменяем стандартное открытие ЗаказаПокупателя
СтандартнаяОбработка = Ложь;
ТекущиеДанные = Элемент.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
СсылкаНаЗаказ = ТекущиеДанные.Ссылка;
ИмяСекции = СтрЗаменить(Колонка.Имя, "Секция_", "");
// Формируем ключ для поиска в кэше
КлючСвязи = Строка(СсылкаНаЗаказ.УникальныйИдентификатор()) + "_" + ИмяСекции;
СсылкаНаСборку = КэшДокументовСборки[КлючСвязи];
Если ЗначениеЗаполнено(СсылкаНаСборку) Тогда
// Открываем форму найденного документа сборки
ОткрытьЗначение(СсылкаНаСборку);
Иначе
Предупреждение("Сборочный лист для секции """ + ИмяСекции + """ еще не создан!", 5, "Внимание");
КонецЕсли;
КонецЕсли;
КонецПроцедуры
---
Если вы хотите, чтобы платформа 1С самостоятельно понимала тип данных в динамической колонке и обрабатывала системные действия (такие как открытие по кнопке "Лупа" или клавише F4), можно использовать явную типизацию колонки табличного поля.
Проанализируем этот подход по шагам:
Истина.ДокументСсылка.СборкаПоСекции).ПриВыводеСтроки или ПриПолученииДанных) передавайте ссылку на документ непосредственно в свойство Значение ячейки, а не в Текст:
// Пример заполнения ячейки при включенном свойстве "СодержитЗначение"
ЯчейкаСекции = ОформлениеСтроки.Ячейки.Секция_А;
ЯчейкаСекции.Значение = СсылкаНаДокументСборки; // Теперь ячейка физически содержит ссылку
ЯчейкаСекции.ОтображатьТекст = Истина;
ЯчейкаСекции.Текст = ?(ЗначениеЗаполнено(СсылкаНаДокументСборки), "Собрано", "Пусто");
При таком подходе в событии Выбор вам достаточно будет написать простой код получения значения активной ячейки:
Процедура ДокументыСписокВыбор(Элемент, Колонка, СтандартнаяОбработка)
Если Колонка.Имя = "Секция_А" Тогда
СтандартнаяОбработка = Ложь;
// Получаем значение напрямую из текущей строки табличного поля
ТекущаяСтрока = Элемент.ТекущаяСтрока;
Если ТекущаяСтрока <> Неопределено Тогда
СсылкаНаСборку = ТекущаяСтрока[Колонка.Имя];
Если ЗначениеЗаполнено(СсылкаНаСборку) Тогда
ОткрытьЗначение(СсылкаНаСборку);
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Важное примечание: Этот способ прост в реализации, однако на практике в обычных формах при интенсивной прокрутке списков значения ячеек динамических колонок могут сбрасываться платформой для экономии памяти. Поэтому первый способ с хранением данных в Соответствие считается более отказоустойчивым.
---
Если ваша конфигурация работает на управляемом приложении (или планируется переход на управляемые формы), то задача решается принципиально иначе — без единой строчки кода для обработки двойного клика.
В управляемом интерфейсе используется объект ДинамическийСписок, работающий на базе механизма компоновки данных. Мы можем настроить автоматическое связывание таблиц на уровне СУБД:
Шаг 1. Настройка запроса динамического списка
В свойствах динамического списка журнала документов включите флаг Произвольный запрос и откройте конструктор запроса. Нам нужно связать журнал документов с таблицей сборки с помощью левого соединения:
ВЫБРАТЬ
Журнал.Ссылка КАК Ссылка,
Журнал.Дата КАК Дата,
Журнал.Номер КАК Номер,
// Левое соединение позволяет получить ссылку на документ сборки для конкретной секции
СборкаСекция1.Ссылка КАК СборкаСекция1,
СборкаСекция2.Ссылка КАК СборкаСекция2
ИЗ
ЖурналДокументов.ЗаказыПокупателей КАК Журнал
ЛЕВОЕ СОЕДИНЕНИЕ Документ.СборкаПоСекции КАК СборкаСекция1
ПО Журнал.Ссылка = СборкаСекция1.ЗаказПокупателя
И СборкаСекция1.Секция = "Секция 1"
ЛЕВОЕ СОЕДИНЕНИЕ Документ.СборкаПоСекции КАК СборкаСекция2
ПО Журнал.Ссылка = СборкаСекция2.ЗаказПокупателя
И СборкаСекция2.Секция = "Секция 2"
Шаг 2. Размещение полей на форме
Вынесите поля СборкаСекция1 и СборкаСекция2 в качестве колонок таблицы формы. Поскольку эти поля имеют тип ДокументСсылка.СборкаПоСекции, платформа 1С автоматически:
Шаг 3. Цветовое оформление без кода
Чтобы раскрасить ячейки (например, зеленым цветом, если сборка проведена), не нужно использовать программный код. Настройте Условное оформление динамического списка в конфигураторе или через свойства формы:
СборкаСекция1.Проведен Равно Истина.СборкаСекция1.Этот подход работает на уровне платформы чрезвычайно быстро, не нагружает клиентский компьютер лишними вычислениями и полностью оптимизирован под веб-клиент и тонкий клиент 1С.